CS318 - Pintos
Pintos source browser for JHU CS318 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. */
38 struct 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. */
50 struct 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. */
58 struct block
59  {
60  struct list_elem free_elem; /**< Free list element. */
61  };
62 
63 /** Our set of descriptors. */
64 static struct desc descs[10]; /**< Descriptors. */
65 static size_t desc_cnt; /**< Number of descriptors. */
66 
67 static struct arena *block_to_arena (struct block *);
68 static struct block *arena_to_block (struct arena *, size_t idx);
69 
70 /** Initializes the malloc() descriptors. */
71 void
72 malloc_init (void)
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. */
89 void *
90 malloc (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;
140  a->free_cnt = d->blocks_per_arena;
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. */
149  b = list_entry (list_pop_front (&d->free_list), struct block, free_elem);
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. */
158 void *
159 calloc (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. */
178 static 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). */
194 void *
195 realloc (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(). */
218 void
219 free (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 
246  ASSERT (a->free_cnt == d->blocks_per_arena);
247  for (i = 0; i < d->blocks_per_arena; i++)
248  {
249  struct block *b = arena_to_block (a, i);
250  list_remove (&b->free_elem);
251  }
252  palloc_free_page (a);
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. */
267 static 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. */
285 static struct block *
286 arena_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 }
malloc
void * malloc(size_t size)
Obtains and returns a new block of at least SIZE bytes.
Definition: malloc.c:90
uint8_t
unsigned char uint8_t
Definition: stdint.h:20
list_elem
Doubly linked list.
Definition: list.h:90
palloc_get_multiple
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
list_entry
#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
lock_release
void lock_release(struct lock *lock)
Releases LOCK, which must be owned by the current thread.
Definition: synch.c:229
NULL
#define NULL
Definition: stddef.h:4
pg_round_down
static void * pg_round_down(const void *va)
Round down to nearest page boundary.
Definition: vaddr.h:39
arena::free_cnt
size_t free_cnt
Free blocks; pages in big block.
Definition: malloc.c:54
arena::desc
struct desc * desc
Owning descriptor, null for big block.
Definition: malloc.c:53
string.h
list_init
void list_init(struct list *list)
Initializes LIST as an empty list.
Definition: list.c:61
lock_init
void lock_init(struct lock *lock)
Initializes LOCK.
Definition: synch.c:176
memcpy
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_to_block
static struct block * arena_to_block(struct arena *, size_t idx)
Returns the (IDX - 1)'th block within arena A.
Definition: malloc.c:286
block::free_elem
struct list_elem free_elem
Free list element.
Definition: malloc.c:60
desc::lock
struct lock lock
Lock.
Definition: malloc.c:43
arena
Arena.
Definition: malloc.c:50
free
void free(void *p)
Frees block P, which must have been previously allocated with malloc(), calloc(), or realloc().
Definition: malloc.c:219
PGSIZE
#define PGSIZE
Bytes in a page.
Definition: vaddr.h:20
palloc_get_page
void * palloc_get_page(enum palloc_flags flags)
Obtains a single free page and returns its kernel virtual address.
Definition: palloc.c:111
block::size
block_sector_t size
Size in sectors.
Definition: block.c:15
block_to_arena
static struct arena * block_to_arena(struct block *)
Returns the arena that block B is inside.
Definition: malloc.c:268
realloc
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
desc_cnt
static size_t desc_cnt
Number of descriptors.
Definition: malloc.c:65
list_remove
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
desc::block_size
size_t block_size
Size of each element in bytes.
Definition: malloc.c:40
ARENA_MAGIC
#define ARENA_MAGIC
Magic number for detecting arena corruption.
Definition: malloc.c:47
palloc_free_multiple
void palloc_free_multiple(void *pages, size_t page_cnt)
Frees the PAGE_CNT pages starting at PAGES.
Definition: palloc.c:118
malloc.h
palloc.h
round.h
calloc
void * calloc(size_t a, size_t b)
Allocates and return A times B bytes initialized to zeroes.
Definition: malloc.c:159
stdint.h
list_empty
bool list_empty(struct list *list)
Returns true if LIST is empty, false otherwise.
Definition: list.c:310
ASSERT
#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
list_pop_front
struct list_elem * list_pop_front(struct list *list)
Removes the front element from LIST and returns it.
Definition: list.c:260
block
A block device.
Definition: block.c:9
arena::magic
unsigned magic
Always set to ARENA_MAGIC.
Definition: malloc.c:52
desc::free_list
struct list free_list
List of free blocks.
Definition: malloc.c:42
desc
A simple implementation of malloc().
Definition: malloc.c:38
list_push_front
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
palloc_free_page
void palloc_free_page(void *page)
Frees the page at PAGE.
Definition: palloc.c:146
DIV_ROUND_UP
#define DIV_ROUND_UP(X, STEP)
Yields X divided by STEP, rounded up.
Definition: round.h:10
memset
void * memset(void *dst_, int value, size_t size)
Sets the SIZE bytes in DST to VALUE.
Definition: string.c:279
desc::blocks_per_arena
size_t blocks_per_arena
Number of blocks in an arena.
Definition: malloc.c:41
list_push_back
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
vaddr.h
block_size
static size_t block_size(void *block)
Returns the number of bytes allocated for BLOCK.
Definition: malloc.c:179
list.h
pg_ofs
static unsigned pg_ofs(const void *va)
Offset within a page.
Definition: vaddr.h:24
lock_acquire
void lock_acquire(struct lock *lock)
Acquires LOCK, sleeping until it becomes available if necessary.
Definition: synch.c:193
synch.h
lock
Lock.
Definition: synch.h:21
list
List.
Definition: list.h:97
debug.h
malloc_init
void malloc_init(void)
Initializes the malloc() descriptors.
Definition: malloc.c:72
descs
static struct desc descs[10]
Our set of descriptors.
Definition: malloc.c:64