PKUOS - Pintos
Pintos source browser for PKU Operating System course
malloc.c
Go to the documentation of this file.
1#include "threads/malloc.h"
2#include <debug.h>
3#include <list.h>
4#include <round.h>
5#include <stdint.h>
6#include <stdio.h>
7#include <string.h>
8#include "threads/palloc.h"
9#include "threads/synch.h"
10#include "threads/vaddr.h"
11
12/** A simple implementation of malloc().
13
14 The size of each request, in bytes, is rounded up to a power
15 of 2 and assigned to the "descriptor" that manages blocks of
16 that size. The descriptor keeps a list of free blocks. If
17 the free list is nonempty, one of its blocks is used to
18 satisfy the request.
19
20 Otherwise, a new page of memory, called an "arena", is
21 obtained from the page allocator (if none is available,
22 malloc() returns a null pointer). The new arena is divided
23 into blocks, all of which are added to the descriptor's free
24 list. Then we return one of the new blocks.
25
26 When we free a block, we add it to its descriptor's free list.
27 But if the arena that the block was in now has no in-use
28 blocks, we remove all of the arena's blocks from the free list
29 and give the arena back to the page allocator.
30
31 We can't handle blocks bigger than 2 kB using this scheme,
32 because they're too big to fit in a single page with a
33 descriptor. We handle those by allocating contiguous pages
34 with the page allocator and sticking the allocation size at
35 the beginning of the allocated block's arena header. */
36
37/** Descriptor. */
38struct desc
39 {
40 size_t block_size; /**< Size of each element in bytes. */
41 size_t blocks_per_arena; /**< Number of blocks in an arena. */
42 struct list free_list; /**< List of free blocks. */
43 struct lock lock; /**< Lock. */
44 };
45
46/** Magic number for detecting arena corruption. */
47#define ARENA_MAGIC 0x9a548eed
48
49/** Arena. */
50struct arena
51 {
52 unsigned magic; /**< Always set to ARENA_MAGIC. */
53 struct desc *desc; /**< Owning descriptor, null for big block. */
54 size_t free_cnt; /**< Free blocks; pages in big block. */
55 };
56
57/** Free block. */
58struct block
59 {
60 struct list_elem free_elem; /**< Free list element. */
61 };
62
63/** Our set of descriptors. */
64static struct desc descs[10]; /**< Descriptors. */
65static size_t desc_cnt; /**< Number of descriptors. */
66
67static struct arena *block_to_arena (struct block *);
68static struct block *arena_to_block (struct arena *, size_t idx);
69
70/** Initializes the malloc() descriptors. */
71void
73{
74 size_t block_size;
75
76 for (block_size = 16; block_size < PGSIZE / 2; block_size *= 2)
77 {
78 struct desc *d = &descs[desc_cnt++];
79 ASSERT (desc_cnt <= sizeof descs / sizeof *descs);
81 d->blocks_per_arena = (PGSIZE - sizeof (struct arena)) / block_size;
82 list_init (&d->free_list);
83 lock_init (&d->lock);
84 }
85}
86
87/** Obtains and returns a new block of at least SIZE bytes.
88 Returns a null pointer if memory is not available. */
89void *
90malloc (size_t size)
91{
92 struct desc *d;
93 struct block *b;
94 struct arena *a;
95
96 /* A null pointer satisfies a request for 0 bytes. */
97 if (size == 0)
98 return NULL;
99
100 /* Find the smallest descriptor that satisfies a SIZE-byte
101 request. */
102 for (d = descs; d < descs + desc_cnt; d++)
103 if (d->block_size >= size)
104 break;
105 if (d == descs + desc_cnt)
106 {
107 /* SIZE is too big for any descriptor.
108 Allocate enough pages to hold SIZE plus an arena. */
109 size_t page_cnt = DIV_ROUND_UP (size + sizeof *a, PGSIZE);
110 a = palloc_get_multiple (0, page_cnt);
111 if (a == NULL)
112 return NULL;
113
114 /* Initialize the arena to indicate a big block of PAGE_CNT
115 pages, and return it. */
116 a->magic = ARENA_MAGIC;
117 a->desc = NULL;
118 a->free_cnt = page_cnt;
119 return a + 1;
120 }
121
122 lock_acquire (&d->lock);
123
124 /* If the free list is empty, create a new arena. */
125 if (list_empty (&d->free_list))
126 {
127 size_t i;
128
129 /* Allocate a page. */
130 a = palloc_get_page (0);
131 if (a == NULL)
132 {
133 lock_release (&d->lock);
134 return NULL;
135 }
136
137 /* Initialize arena and add its blocks to the free list. */
138 a->magic = ARENA_MAGIC;
139 a->desc = d;
141 for (i = 0; i < d->blocks_per_arena; i++)
142 {
143 struct block *b = arena_to_block (a, i);
145 }
146 }
147
148 /* Get a block from free list and return it. */
150 a = block_to_arena (b);
151 a->free_cnt--;
152 lock_release (&d->lock);
153 return b;
154}
155
156/** Allocates and return A times B bytes initialized to zeroes.
157 Returns a null pointer if memory is not available. */
158void *
159calloc (size_t a, size_t b)
160{
161 void *p;
162 size_t size;
163
164 /* Calculate block size and make sure it fits in size_t. */
165 size = a * b;
166 if (size < a || size < b)
167 return NULL;
168
169 /* Allocate and zero memory. */
170 p = malloc (size);
171 if (p != NULL)
172 memset (p, 0, size);
173
174 return p;
175}
176
177/** Returns the number of bytes allocated for BLOCK. */
178static size_t
180{
181 struct block *b = block;
182 struct arena *a = block_to_arena (b);
183 struct desc *d = a->desc;
184
185 return d != NULL ? d->block_size : PGSIZE * a->free_cnt - pg_ofs (block);
186}
187
188/** Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly
189 moving it in the process.
190 If successful, returns the new block; on failure, returns a
191 null pointer.
192 A call with null OLD_BLOCK is equivalent to malloc(NEW_SIZE).
193 A call with zero NEW_SIZE is equivalent to free(OLD_BLOCK). */
194void *
195realloc (void *old_block, size_t new_size)
196{
197 if (new_size == 0)
198 {
199 free (old_block);
200 return NULL;
201 }
202 else
203 {
204 void *new_block = malloc (new_size);
205 if (old_block != NULL && new_block != NULL)
206 {
207 size_t old_size = block_size (old_block);
208 size_t min_size = new_size < old_size ? new_size : old_size;
209 memcpy (new_block, old_block, min_size);
210 free (old_block);
211 }
212 return new_block;
213 }
214}
215
216/** Frees block P, which must have been previously allocated with
217 malloc(), calloc(), or realloc(). */
218void
219free (void *p)
220{
221 if (p != NULL)
222 {
223 struct block *b = p;
224 struct arena *a = block_to_arena (b);
225 struct desc *d = a->desc;
226
227 if (d != NULL)
228 {
229 /* It's a normal block. We handle it here. */
230
231#ifndef NDEBUG
232 /* Clear the block to help detect use-after-free bugs. */
233 memset (b, 0xcc, d->block_size);
234#endif
235
236 lock_acquire (&d->lock);
237
238 /* Add block to free list. */
240
241 /* If the arena is now entirely unused, free it. */
242 if (++a->free_cnt >= d->blocks_per_arena)
243 {
244 size_t i;
245
247 for (i = 0; i < d->blocks_per_arena; i++)
248 {
249 struct block *b = arena_to_block (a, i);
251 }
253 }
254
255 lock_release (&d->lock);
256 }
257 else
258 {
259 /* It's a big block. Free its pages. */
261 return;
262 }
263 }
264}
265
266/** Returns the arena that block B is inside. */
267static struct arena *
269{
270 struct arena *a = pg_round_down (b);
271
272 /* Check that the arena is valid. */
273 ASSERT (a != NULL);
274 ASSERT (a->magic == ARENA_MAGIC);
275
276 /* Check that the block is properly aligned for the arena. */
277 ASSERT (a->desc == NULL
278 || (pg_ofs (b) - sizeof *a) % a->desc->block_size == 0);
279 ASSERT (a->desc != NULL || pg_ofs (b) == sizeof *a);
280
281 return a;
282}
283
284/** Returns the (IDX - 1)'th block within arena A. */
285static struct block *
286arena_to_block (struct arena *a, size_t idx)
287{
288 ASSERT (a != NULL);
289 ASSERT (a->magic == ARENA_MAGIC);
290 ASSERT (idx < a->desc->blocks_per_arena);
291 return (struct block *) ((uint8_t *) a
292 + sizeof *a
293 + idx * a->desc->block_size);
294}
#define ASSERT(CONDITION)
This is outside the header guard so that debug.h may be included multiple times with different settin...
Definition: debug.h:31
struct list_elem * list_remove(struct list_elem *elem)
Removes ELEM from its list and returns the element that followed it.
Definition: list.c:249
struct list_elem * list_pop_front(struct list *list)
Removes the front element from LIST and returns it.
Definition: list.c:260
void list_push_front(struct list *list, struct list_elem *elem)
Inserts ELEM at the beginning of LIST, so that it becomes the front in LIST.
Definition: list.c:209
bool list_empty(struct list *list)
Returns true if LIST is empty, false otherwise.
Definition: list.c:310
void list_init(struct list *list)
Initializes LIST as an empty list.
Definition: list.c:61
void list_push_back(struct list *list, struct list_elem *elem)
Inserts ELEM at the end of LIST, so that it becomes the back in LIST.
Definition: list.c:217
#define list_entry(LIST_ELEM, STRUCT, MEMBER)
Converts pointer to list element LIST_ELEM into a pointer to the structure that LIST_ELEM is embedded...
Definition: list.h:108
void malloc_init(void)
Initializes the malloc() descriptors.
Definition: malloc.c:72
static size_t desc_cnt
Number of descriptors.
Definition: malloc.c:65
static size_t block_size(void *block)
Returns the number of bytes allocated for BLOCK.
Definition: malloc.c:179
static struct arena * block_to_arena(struct block *)
Returns the arena that block B is inside.
Definition: malloc.c:268
void * realloc(void *old_block, size_t new_size)
Attempts to resize OLD_BLOCK to NEW_SIZE bytes, possibly moving it in the process.
Definition: malloc.c:195
#define ARENA_MAGIC
Magic number for detecting arena corruption.
Definition: malloc.c:47
void * calloc(size_t a, size_t b)
Allocates and return A times B bytes initialized to zeroes.
Definition: malloc.c:159
static struct block * arena_to_block(struct arena *, size_t idx)
Returns the (IDX - 1)'th block within arena A.
Definition: malloc.c:286
void * malloc(size_t size)
Obtains and returns a new block of at least SIZE bytes.
Definition: malloc.c:90
void free(void *p)
Frees block P, which must have been previously allocated with malloc(), calloc(), or realloc().
Definition: malloc.c:219
static struct desc descs[10]
Our set of descriptors.
Definition: malloc.c:64
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
void * palloc_get_multiple(enum palloc_flags flags, size_t page_cnt)
Obtains and returns a group of PAGE_CNT contiguous free pages.
Definition: palloc.c:71
void palloc_free_page(void *page)
Frees the page at PAGE.
Definition: palloc.c:146
void palloc_free_multiple(void *pages, size_t page_cnt)
Frees the PAGE_CNT pages starting at PAGES.
Definition: palloc.c:118
#define DIV_ROUND_UP(X, STEP)
Yields X divided by STEP, rounded up.
Definition: round.h:10
#define NULL
Definition: stddef.h:4
unsigned char uint8_t
Definition: stdint.h:20
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
void * memcpy(void *dst_, const void *src_, size_t size)
Copies SIZE bytes from SRC to DST, which must not overlap.
Definition: string.c:7
Arena.
Definition: malloc.c:51
size_t free_cnt
Free blocks; pages in big block.
Definition: malloc.c:54
unsigned magic
Always set to ARENA_MAGIC.
Definition: malloc.c:52
struct desc * desc
Owning descriptor, null for big block.
Definition: malloc.c:53
A block device.
Definition: block.c:10
block_sector_t size
Size in sectors.
Definition: block.c:15
struct list_elem free_elem
Free list element.
Definition: malloc.c:60
A simple implementation of malloc().
Definition: malloc.c:39
size_t block_size
Size of each element in bytes.
Definition: malloc.c:40
struct list free_list
List of free blocks.
Definition: malloc.c:42
struct lock lock
Lock.
Definition: malloc.c:43
size_t blocks_per_arena
Number of blocks in an arena.
Definition: malloc.c:41
Doubly linked list.
Definition: list.h:91
List.
Definition: list.h:98
Lock.
Definition: synch.h:22
void lock_release(struct lock *lock)
Releases LOCK, which must be owned by the current thread.
Definition: synch.c:229
void lock_init(struct lock *lock)
Initializes LOCK.
Definition: synch.c:176
void lock_acquire(struct lock *lock)
Acquires LOCK, sleeping until it becomes available if necessary.
Definition: synch.c:193
static void * pg_round_down(const void *va)
Round down to nearest page boundary.
Definition: vaddr.h:39
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
static unsigned pg_ofs(const void *va)
Offset within a page.
Definition: vaddr.h:24