| 141 |
Kevin |
1 |
/*
|
|
|
2 |
* Simple, 32-bit and 64-bit clean allocator based on implicit free
|
|
|
3 |
* lists, first fit placement, and boundary tag coalescing, as described
|
|
|
4 |
* in the CS:APP2e text. Blocks must be aligned to doubleword (8 byte)
|
|
|
5 |
* boundaries. Minimum block size is 16 bytes.
|
|
|
6 |
*
|
|
|
7 |
* This version is loosely based on
|
|
|
8 |
* http://csapp.cs.cmu.edu/public/ics2/code/vm/malloc/mm.c
|
|
|
9 |
* but unlike the book's version, it does not use C preprocessor
|
|
|
10 |
* macros or explicit bit operations.
|
|
|
11 |
*/
|
|
|
12 |
#include <stdio.h>
|
|
|
13 |
#include <string.h>
|
|
|
14 |
#include <stdlib.h>
|
|
|
15 |
#include <stdbool.h>
|
|
|
16 |
#include <stdint.h>
|
|
|
17 |
#include <stddef.h>
|
|
|
18 |
#include <assert.h>
|
|
|
19 |
|
|
|
20 |
#include "mm.h"
|
|
|
21 |
#include "memlib.h"
|
|
|
22 |
|
|
|
23 |
struct boundary_tag {
|
|
|
24 |
int inuse:1; // inuse bit
|
|
|
25 |
int size:31; // size of block, in words
|
|
|
26 |
};
|
|
|
27 |
|
|
|
28 |
/* FENCE is used for heap prologue/epilogue. */
|
|
|
29 |
const struct boundary_tag FENCE = { .inuse = 1, .size = 0 };
|
|
|
30 |
|
|
|
31 |
/* A C struct describing the beginning of each block.
|
|
|
32 |
* For implicit lists, used and free blocks have the same
|
|
|
33 |
* structure, so one struct will suffice for this example.
|
|
|
34 |
* If each block is aligned at 4 mod 8, each payload will
|
|
|
35 |
* be aligned at 0 mod 8.
|
|
|
36 |
*/
|
|
|
37 |
struct block {
|
|
|
38 |
struct boundary_tag header; /* offset 0, at address 4 mod 8 */
|
|
|
39 |
char payload[0]; /* offset 4, at address 0 mod 8 */
|
|
|
40 |
};
|
|
|
41 |
|
|
|
42 |
/*
|
|
|
43 |
* If NEXT_FIT defined use next fit search, else use first fit search
|
|
|
44 |
*/
|
|
|
45 |
#define NEXT_FITx
|
|
|
46 |
|
|
|
47 |
/* Basic constants and macros */
|
|
|
48 |
#define WSIZE 4 /* Word and header/footer size (bytes) */
|
|
|
49 |
#define DSIZE 8 /* Doubleword size (bytes) */
|
|
|
50 |
#define MIN_BLOCK_SIZE_WORDS 4 /* Minimum block size in words */
|
|
|
51 |
#define CHUNKSIZE (1<<10) /* Extend heap by this amount (words) */
|
|
|
52 |
|
|
|
53 |
#define MAX(x, y) ((x) > (y)? (x) : (y))
|
|
|
54 |
|
|
|
55 |
/* Global variables */
|
|
|
56 |
static struct block *heap_listp = 0; /* Pointer to first block */
|
|
|
57 |
#ifdef NEXT_FIT
|
|
|
58 |
static struct block *rover; /* Next fit rover */
|
|
|
59 |
#endif
|
|
|
60 |
|
|
|
61 |
/* Function prototypes for internal helper routines */
|
|
|
62 |
static struct block *extend_heap(size_t words);
|
|
|
63 |
static void place(struct block *bp, size_t asize);
|
|
|
64 |
static struct block *find_fit(size_t asize);
|
|
|
65 |
static struct block *coalesce(struct block *bp);
|
|
|
66 |
|
|
|
67 |
/* Given a block, obtain previous's block footer.
|
|
|
68 |
Works for left-most block also. */
|
|
|
69 |
static struct boundary_tag * prev_blk_footer(struct block *blk) {
|
|
|
70 |
return &blk->header - 1;
|
|
|
71 |
}
|
|
|
72 |
|
|
|
73 |
/* Return if block is free */
|
|
|
74 |
static bool blk_free(struct block *blk) {
|
|
|
75 |
return !blk->header.inuse;
|
|
|
76 |
}
|
|
|
77 |
|
|
|
78 |
/* Return size of block is free */
|
|
|
79 |
static size_t blk_size(struct block *blk) {
|
|
|
80 |
return blk->header.size;
|
|
|
81 |
}
|
|
|
82 |
|
|
|
83 |
/* Given a block, obtain pointer to previous block.
|
|
|
84 |
Not meaningful for left-most block. */
|
|
|
85 |
static struct block *prev_blk(struct block *blk) {
|
|
|
86 |
struct boundary_tag *prevfooter = prev_blk_footer(blk);
|
|
|
87 |
assert(prevfooter->size != 0);
|
|
|
88 |
return (struct block *)((size_t *)blk - prevfooter->size);
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
/* Given a block, obtain pointer to next block.
|
|
|
92 |
Not meaningful for right-most block. */
|
|
|
93 |
static struct block *next_blk(struct block *blk) {
|
|
|
94 |
assert(blk_size(blk) != 0);
|
|
|
95 |
return (struct block *)((size_t *)blk + blk->header.size);
|
|
|
96 |
}
|
|
|
97 |
|
|
|
98 |
/* Given a block, obtain its footer boundary tag */
|
|
|
99 |
static struct boundary_tag * get_footer(struct block *blk) {
|
|
|
100 |
return (void *)((size_t *)blk + blk->header.size)
|
|
|
101 |
- sizeof(struct boundary_tag);
|
|
|
102 |
}
|
|
|
103 |
|
|
|
104 |
/* Set a block's size and inuse bit in header and footer */
|
|
|
105 |
static void set_header_and_footer(struct block *blk, int size, int inuse) {
|
|
|
106 |
blk->header.inuse = inuse;
|
|
|
107 |
blk->header.size = size;
|
|
|
108 |
* get_footer(blk) = blk->header; /* Copy header to footer */
|
|
|
109 |
}
|
|
|
110 |
|
|
|
111 |
/* Mark a block as used and set its size. */
|
|
|
112 |
static void mark_block_used(struct block *blk, int size) {
|
|
|
113 |
set_header_and_footer(blk, size, 1);
|
|
|
114 |
}
|
|
|
115 |
|
|
|
116 |
/* Mark a block as free and set its size. */
|
|
|
117 |
static void mark_block_free(struct block *blk, int size) {
|
|
|
118 |
set_header_and_footer(blk, size, 0);
|
|
|
119 |
}
|
|
|
120 |
|
|
|
121 |
/*
|
|
|
122 |
* mm_init - Initialize the memory manager
|
|
|
123 |
*/
|
|
|
124 |
int mm_init(void)
|
|
|
125 |
{
|
|
|
126 |
/* Create the initial empty heap */
|
|
|
127 |
struct boundary_tag * initial = mem_sbrk(2 * sizeof(struct boundary_tag));
|
|
|
128 |
if (initial == (void *)-1)
|
|
|
129 |
return -1;
|
|
|
130 |
|
|
|
131 |
/* We use a slightly different strategy than suggested in the book.
|
|
|
132 |
* Rather than placing a min-sized prologue block at the beginning
|
|
|
133 |
* of the heap, we simply place two fences.
|
|
|
134 |
* The consequence is that coalesce() must call prev_blk_footer()
|
|
|
135 |
* and not prev_blk() - prev_blk() cannot be called on the left-most
|
|
|
136 |
* block.
|
|
|
137 |
*/
|
|
|
138 |
initial[0] = FENCE; /* Prologue footer */
|
|
|
139 |
heap_listp = (struct block *)&initial[1];
|
|
|
140 |
initial[1] = FENCE; /* Epilogue header */
|
|
|
141 |
|
|
|
142 |
#ifdef NEXT_FIT
|
|
|
143 |
rover = heap_listp;
|
|
|
144 |
#endif
|
|
|
145 |
|
|
|
146 |
/* Extend the empty heap with a free block of CHUNKSIZE bytes */
|
|
|
147 |
if (extend_heap(CHUNKSIZE) == NULL)
|
|
|
148 |
return -1;
|
|
|
149 |
return 0;
|
|
|
150 |
}
|
|
|
151 |
|
|
|
152 |
/*
|
|
|
153 |
* mm_malloc - Allocate a block with at least size bytes of payload
|
|
|
154 |
*/
|
|
|
155 |
void *mm_malloc(size_t size)
|
|
|
156 |
{
|
|
|
157 |
size_t awords; /* Adjusted block size in words */
|
|
|
158 |
size_t extendwords; /* Amount to extend heap if no fit */
|
|
|
159 |
struct block *bp;
|
|
|
160 |
|
|
|
161 |
if (heap_listp == 0){
|
|
|
162 |
mm_init();
|
|
|
163 |
}
|
|
|
164 |
/* Ignore spurious requests */
|
|
|
165 |
if (size == 0)
|
|
|
166 |
return NULL;
|
|
|
167 |
|
|
|
168 |
/* Adjust block size to include overhead and alignment reqs. */
|
|
|
169 |
size += 2 * sizeof(struct boundary_tag); /* account for tags */
|
|
|
170 |
size = (size + DSIZE - 1) & ~(DSIZE - 1); /* align to double word */
|
|
|
171 |
awords = MAX(MIN_BLOCK_SIZE_WORDS, size/WSIZE);
|
|
|
172 |
/* respect minimum size */
|
|
|
173 |
|
|
|
174 |
/* Search the free list for a fit */
|
|
|
175 |
if ((bp = find_fit(awords)) != NULL) {
|
|
|
176 |
place(bp, awords);
|
|
|
177 |
return bp->payload;
|
|
|
178 |
}
|
|
|
179 |
|
|
|
180 |
/* No fit found. Get more memory and place the block */
|
|
|
181 |
extendwords = MAX(awords,CHUNKSIZE);
|
|
|
182 |
if ((bp = extend_heap(extendwords)) == NULL)
|
|
|
183 |
return NULL;
|
|
|
184 |
place(bp, awords);
|
|
|
185 |
return bp->payload;
|
|
|
186 |
}
|
|
|
187 |
|
|
|
188 |
/*
|
|
|
189 |
* mm_free - Free a block
|
|
|
190 |
*/
|
|
|
191 |
void mm_free(void *bp)
|
|
|
192 |
{
|
|
|
193 |
if (bp == 0)
|
|
|
194 |
return;
|
|
|
195 |
|
|
|
196 |
/* Find block from user pointer */
|
|
|
197 |
struct block *blk = bp - offsetof(struct block, payload);
|
|
|
198 |
if (heap_listp == 0) {
|
|
|
199 |
mm_init();
|
|
|
200 |
}
|
|
|
201 |
|
|
|
202 |
mark_block_free(blk, blk_size(blk));
|
|
|
203 |
coalesce(blk);
|
|
|
204 |
}
|
|
|
205 |
|
|
|
206 |
/*
|
|
|
207 |
* coalesce - Boundary tag coalescing. Return ptr to coalesced block
|
|
|
208 |
*/
|
|
|
209 |
static struct block *coalesce(struct block *bp)
|
|
|
210 |
{
|
|
|
211 |
bool prev_alloc = prev_blk_footer(bp)->inuse;
|
|
|
212 |
bool next_alloc = ! blk_free(next_blk(bp));
|
|
|
213 |
size_t size = blk_size(bp);
|
|
|
214 |
|
|
|
215 |
if (prev_alloc && next_alloc) { /* Case 1 */
|
|
|
216 |
return bp;
|
|
|
217 |
}
|
|
|
218 |
|
|
|
219 |
else if (prev_alloc && !next_alloc) { /* Case 2 */
|
|
|
220 |
mark_block_free(bp, size + blk_size(next_blk(bp)));
|
|
|
221 |
}
|
|
|
222 |
|
|
|
223 |
else if (!prev_alloc && next_alloc) { /* Case 3 */
|
|
|
224 |
bp = prev_blk(bp);
|
|
|
225 |
mark_block_free(bp, size + blk_size(bp));
|
|
|
226 |
}
|
|
|
227 |
|
|
|
228 |
else { /* Case 4 */
|
|
|
229 |
mark_block_free(prev_blk(bp),
|
|
|
230 |
size + blk_size(next_blk(bp)) + blk_size(prev_blk(bp)));
|
|
|
231 |
bp = prev_blk(bp);
|
|
|
232 |
}
|
|
|
233 |
#ifdef NEXT_FIT
|
|
|
234 |
/* Make sure the rover isn't pointing into the free block */
|
|
|
235 |
/* that we just coalesced */
|
|
|
236 |
if ((rover > bp) && (rover < next_blk(bp)))
|
|
|
237 |
rover = bp;
|
|
|
238 |
#endif
|
|
|
239 |
return bp;
|
|
|
240 |
}
|
|
|
241 |
|
|
|
242 |
/*
|
|
|
243 |
* mm_realloc - Naive implementation of realloc
|
|
|
244 |
*/
|
|
|
245 |
void *mm_realloc(void *ptr, size_t size)
|
|
|
246 |
{
|
|
|
247 |
size_t oldsize;
|
|
|
248 |
void *newptr;
|
|
|
249 |
|
|
|
250 |
/* If size == 0 then this is just free, and we return NULL. */
|
|
|
251 |
if(size == 0) {
|
|
|
252 |
mm_free(ptr);
|
|
|
253 |
return 0;
|
|
|
254 |
}
|
|
|
255 |
|
|
|
256 |
/* If oldptr is NULL, then this is just malloc. */
|
|
|
257 |
if(ptr == NULL) {
|
|
|
258 |
return mm_malloc(size);
|
|
|
259 |
}
|
|
|
260 |
|
|
|
261 |
newptr = mm_malloc(size);
|
|
|
262 |
|
|
|
263 |
/* If realloc() fails the original block is left untouched */
|
|
|
264 |
if(!newptr) {
|
|
|
265 |
return 0;
|
|
|
266 |
}
|
|
|
267 |
|
|
|
268 |
/* Copy the old data. */
|
|
|
269 |
struct block *oldblock = ptr - offsetof(struct block, payload);
|
|
|
270 |
oldsize = blk_size(oldblock) * WSIZE;
|
|
|
271 |
if(size < oldsize) oldsize = size;
|
|
|
272 |
memcpy(newptr, ptr, oldsize);
|
|
|
273 |
|
|
|
274 |
/* Free the old block. */
|
|
|
275 |
mm_free(ptr);
|
|
|
276 |
|
|
|
277 |
return newptr;
|
|
|
278 |
}
|
|
|
279 |
|
|
|
280 |
/*
|
|
|
281 |
* checkheap - We don't check anything right now.
|
|
|
282 |
*/
|
|
|
283 |
void mm_checkheap(int verbose)
|
|
|
284 |
{
|
|
|
285 |
}
|
|
|
286 |
|
|
|
287 |
/*
|
|
|
288 |
* The remaining routines are internal helper routines
|
|
|
289 |
*/
|
|
|
290 |
|
|
|
291 |
/*
|
|
|
292 |
* extend_heap - Extend heap with free block and return its block pointer
|
|
|
293 |
*/
|
|
|
294 |
static struct block *extend_heap(size_t words)
|
|
|
295 |
{
|
|
|
296 |
void *bp;
|
|
|
297 |
|
|
|
298 |
/* Allocate an even number of words to maintain alignment */
|
|
|
299 |
words = (words + 1) & ~1;
|
|
|
300 |
if ((long)(bp = mem_sbrk(words * WSIZE)) == -1)
|
|
|
301 |
return NULL;
|
|
|
302 |
|
|
|
303 |
/* Initialize free block header/footer and the epilogue header.
|
|
|
304 |
* Note that we scoop up the previous epilogue here. */
|
|
|
305 |
struct block * blk = bp - sizeof(FENCE);
|
|
|
306 |
mark_block_free(blk, words);
|
|
|
307 |
next_blk(blk)->header = FENCE;
|
|
|
308 |
|
|
|
309 |
/* Coalesce if the previous block was free */
|
|
|
310 |
return coalesce(blk);
|
|
|
311 |
}
|
|
|
312 |
|
|
|
313 |
/*
|
|
|
314 |
* place - Place block of asize words at start of free block bp
|
|
|
315 |
* and split if remainder would be at least minimum block size
|
|
|
316 |
*/
|
|
|
317 |
static void place(struct block *bp, size_t asize)
|
|
|
318 |
{
|
|
|
319 |
size_t csize = blk_size(bp);
|
|
|
320 |
|
|
|
321 |
if ((csize - asize) >= MIN_BLOCK_SIZE_WORDS) {
|
|
|
322 |
mark_block_used(bp, asize);
|
|
|
323 |
bp = next_blk(bp);
|
|
|
324 |
mark_block_free(bp, csize-asize);
|
|
|
325 |
}
|
|
|
326 |
else {
|
|
|
327 |
mark_block_used(bp, csize);
|
|
|
328 |
}
|
|
|
329 |
}
|
|
|
330 |
|
|
|
331 |
/*
|
|
|
332 |
* find_fit - Find a fit for a block with asize words
|
|
|
333 |
*/
|
|
|
334 |
static struct block *find_fit(size_t asize)
|
|
|
335 |
{
|
|
|
336 |
|
|
|
337 |
#ifdef NEXT_FIT
|
|
|
338 |
/* Next fit search */
|
|
|
339 |
struct block *oldrover = rover;
|
|
|
340 |
|
|
|
341 |
/* Search from the rover to the end of list */
|
|
|
342 |
for ( ; blk_size(rover) > 0; rover = next_blk(rover))
|
|
|
343 |
if (blk_free(rover) && (asize <= blk_size(rover)))
|
|
|
344 |
return rover;
|
|
|
345 |
|
|
|
346 |
/* search from start of list to old rover */
|
|
|
347 |
for (rover = heap_listp; rover < oldrover; rover = next_blk(rover))
|
|
|
348 |
if (blk_free(rover) && (asize <= blk_size(rover)))
|
|
|
349 |
return rover;
|
|
|
350 |
|
|
|
351 |
return NULL; /* no fit found */
|
|
|
352 |
#else
|
|
|
353 |
/* First fit search */
|
|
|
354 |
struct block *bp;
|
|
|
355 |
|
|
|
356 |
for (bp = heap_listp; blk_size(bp) > 0; bp = next_blk(bp)) {
|
|
|
357 |
if (blk_free(bp) && asize <= blk_size(bp)) {
|
|
|
358 |
return bp;
|
|
|
359 |
}
|
|
|
360 |
}
|
|
|
361 |
return NULL; /* No fit */
|
|
|
362 |
#endif
|
|
|
363 |
}
|
|
|
364 |
|
|
|
365 |
team_t team = {
|
|
|
366 |
/* Team name */
|
|
|
367 |
"Sample allocator using implicit lists",
|
|
|
368 |
/* First member's full name */
|
|
|
369 |
"Godmar Back",
|
|
|
370 |
"gback@cs.vt.edu",
|
|
|
371 |
/* Second member's full name (leave blank if none) */
|
|
|
372 |
"",
|
|
|
373 |
"",
|
|
|
374 |
};
|