Subversion Repositories Code-Repo

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
141 Kevin 1
/*
2
 * This implementation of a memory allocation utilizes a segregated fits
3
 * appraoch. Multiple buckets are used for each size range and a doubly
4
 * linked list used to hold the list of free blocks for each bucket.
5
 * Free blocks for each bucket is choosen based on first fit, with the
6
 * search going into the next sized bucket if no fit is found in the
7
 * current bucket. If no free blocks are found that holds the requested
8
 * size, more memory is requested by incrementing the brk pointer using
9
 * mem_sbrk. The requested memory is in a multiple of the page size for
10
 * higher allocating efficiency. Freeing a previously allocated block
11
 * coalesces the block with the previous and next blocks if they are
12
 * empty. The last 4 bytes in each block is used as a boundary tag that
13
 * holds the size of the block it is in. This allows the code to 
14
 * traverse backwards in the address space and check if the previous
15
 * block is empty or allocated.
16
 */
17
#include <stdio.h>
18
#include <stdlib.h>
19
#include <assert.h>
20
#include <unistd.h>
21
#include <string.h>
22
#include <stddef.h>
23
#include <limits.h>
24
 
25
#include "mm.h"
26
#include "memlib.h"
27
#include "list.h"
28
#include "config.h"             /* defines ALIGNMENT */
29
 
30
#define FREE_SPACE_SPLIT_OVERHEAD 512
31
 
32
/*********************************************************
33
 * NOTE TO STUDENTS: Before you do anything else, please
34
 * provide your team information in the following struct.
35
 ********************************************************/
36
team_t team = {
37
    /* Team name */
38
    "Team K",
39
    /* First member's full name */
40
    "Kevin Lee",
41
    /* First member's SLO (@cs.vt.edu) email address */
42
    "klee482@vt.edu",
43
    /* Second member's full name (leave blank if none) */
44
    "",
45
    /* Second member's SLO (@cs.vt.edu) email address (leave blank if none) */
46
    ""
47
};
48
 
49
/* 
50
 * If size is a multiple of ALIGNMENT, return size.
51
 * Else, return next larger multiple of ALIGNMENT:
52
 * (size/ALIGNMENT + 1) * ALIGNMENT
53
 * Does so without requiring integer division, assuming
54
 * ALIGNMENT is a power of 2.
55
 */
56
static size_t roundup(size_t size)
57
{
58
    return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
59
}
60
 
61
/* 
62
 * This C struct captures an allocated header.
63
 *
64
 * By casting a memory location to a pointer to a allocated_block_header,
65
 * we are able to treat a part of memory as if a header had been allocated
66
 * in it.
67
 *
68
 * Note: you should never define instances of 'struct allocated_block_header' -
69
 *       all accesses will be through pointers.
70
 */
71
struct allocated_block_header {
72
    size_t      size;   // Size of the block, includes header and padding
73
 
74
    /* 
75
     * Zero length arrays do not add size to the structure, they simply
76
     * provide a syntactic form to refer to a char array following the
77
     * structure.
78
     * See http://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
79
     *
80
     * The 'aligned' attribute forces 'payload' to be aligned at a
81
     * multiple of alignment, counted from the beginning of the struct
82
     * See http://gcc.gnu.org/onlinedocs/gcc/Variable-Attributes.html
83
     */
84
    char        payload[0] __attribute__((aligned(ALIGNMENT)));
85
};
86
 
87
/* 
88
 * This C struct captures a free header.
89
 *
90
 * By casting a memory location to a pointer to a free_block_header,
91
 * we are able to treat a part of memory as if a header had been allocated
92
 * in it.
93
 */
94
struct free_block_header {
95
    size_t      size;               // Size of the block, includes header and padding
96
    struct      list_elem elem;     // Allows the struct to be used in a doublely linked list
97
};
98
 
99
/* 
100
 * This C struct captures a bucket of free blocks
101
 *
102
 * This struct is an element of a list that holds list of free blocks as
103
 * well as relevant information for the bucket (size of free blocks).
104
 */
105
struct free_list_index {
106
    struct      list free_block_list;   // List of free blocks
107
    int         size_min;               // Minimum size of each block
108
    int         size_max;               // Maximum size of each block
109
    struct      list_elem elem;          // Allows the struct to be used in a doublely linked list
110
};
111
 
112
/* 
113
 * This C struct captures a boundary tag
114
 *
115
 * By casting a memory location to a pointer to a boundary tag,
116
 * we can read the size of the block from the front or the back.
117
 */
118
struct boundary_block {
119
    size_t size;
120
};
121
 
122
// Global list holding the main list of buckets and each bucket's list
123
struct list index_of_free_lists;
124
struct free_list_index bucket_1 = {.size_min = 1, .size_max = 2};
125
struct free_list_index bucket_2 = {.size_min = 3, .size_max = 8};
126
struct free_list_index bucket_3 = {.size_min = 9, .size_max = 32};
127
struct free_list_index bucket_4 = {.size_min = 33, .size_max = 128};
128
struct free_list_index bucket_5 = {.size_min = 129, .size_max = 512};
129
struct free_list_index bucket_6 = {.size_min = 513, .size_max = 1024};
130
struct free_list_index bucket_7 = {.size_min = 1025, .size_max = 2048};
131
struct free_list_index bucket_8 = {.size_min = 2049, .size_max = 4096};
132
struct free_list_index bucket_9 = {.size_min = 4097, .size_max = 8192};
133
struct free_list_index bucket_10 = {.size_min = 8193, .size_max = 16384};
134
struct free_list_index bucket_11 = {.size_min = 16385, .size_max = 32768};
135
struct free_list_index bucket_12 = {.size_min = 32769, .size_max = 65536};
136
struct free_list_index bucket_13 = {.size_min = 65537, .size_max = INT_MAX};
137
 
138
/*
139
 * alloc_block_set_boundary_tags - Sets the boundary tags for allocated blocks
140
 */
141
void alloc_block_set_boundary_tags(void *alloc_block) {
142
    struct allocated_block_header *block = alloc_block;
143
    int size = block->size;
144
    block->size |= 1;
145
 
146
    struct boundary_block *f = alloc_block + size - sizeof(size_t);
147
 
148
    f->size = size |= 1;
149
}
150
 
151
/*
152
 * free_block_set_boundary_tags - Sets the boundary tags for free blocks
153
 */
154
void free_block_set_boundary_tags(void *free_block) {
155
    struct free_block_header *block = free_block;
156
 
157
    struct boundary_block *f = free_block + block->size - sizeof(size_t);
158
 
159
    f->size = block->size;
160
}
161
 
162
/*
163
 * free_block_insert - Takes a free block and pushes it to the front of the
164
 *      correct list that it fits in. Also coalesces the block with the blocks
165
 *      before and after it in the address space.
166
 */
167
void free_block_insert(void *free_block) {
168
    struct free_block_header *block = free_block;
169
 
170
    // Check if the block can be combined with any free blocks before or after
171
    bool prev_block_empty = false;
172
    bool next_block_empty = false;
173
 
174
    struct boundary_block *prev = free_block - sizeof(size_t);
175
    struct boundary_block *next = free_block + block->size;
176
 
177
    if (free_block != mem_heap_lo()) {   
178
        if ((prev->size & 1) == 0) {
179
            prev_block_empty = true;
180
        }
181
    }
182
 
183
    if (free_block + block->size != mem_heap_hi() + 1) {
184
        if ((next->size & 1) == 0) {
185
            next_block_empty = true;
186
        }
187
    }
188
 
189
    // Combine blocks accordingly
190
    if (prev_block_empty && next_block_empty) {
191
        // Both previous and next blocks are empty
192
        struct free_block_header *prev_block = free_block - prev->size;
193
        struct free_block_header *next_block = free_block + block->size;
194
        list_remove(&prev_block->elem); 
195
        list_remove(&next_block->elem);
196
        prev_block->size += block->size;
197
        prev_block->size += next_block->size;
198
        block = prev_block;
199
    } else if (prev_block_empty && !next_block_empty) {
200
        // Only the previous block is empty
201
        struct free_block_header *prev_block = free_block - prev->size;
202
        list_remove(&prev_block->elem); 
203
        prev_block->size += block->size;
204
        block = prev_block;
205
    } else if (!prev_block_empty && next_block_empty) {
206
        // Only the next block is empty
207
        struct free_block_header *next_block = free_block + block->size;
208
        list_remove(&next_block->elem);
209
        block->size += next_block->size;
210
    }
211
 
212
    free_block_set_boundary_tags(block);
213
 
214
    // Insert the free block into the bucket that it belongs to
215
    struct list_elem *index = list_begin(&index_of_free_lists);
216
    while (index != list_end(&index_of_free_lists)) {
217
        struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
218
 
219
        if (block->size >= index_entry->size_min && block->size <= index_entry->size_max) {
220
            list_push_front(&index_entry->free_block_list, &block->elem);
221
            return;
222
        }
223
 
224
        index = list_next(index);
225
    }
226
}
227
 
228
/*
229
 * free_block_alloc - Uses the free space specified in free_block_header to
230
 *      hold a new allocated block of size alloc_size.
231
 */
232
void *free_block_alloc(void *free_block, size_t alloc_size, bool block_in_list) {
233
    struct free_block_header *block = free_block;
234
    if (block_in_list)
235
        list_remove(&block->elem);
236
 
237
    // Before we split up the free block, we need to check if there is enough 
238
    //  space for holding another free block header.
239
    if (block->size - alloc_size > sizeof(struct free_block_header)+FREE_SPACE_SPLIT_OVERHEAD) {
240
        // There is enough space for another free block + overhead
241
        int combined_size = block->size;
242
        block = free_block + alloc_size;
243
        block->size = combined_size - alloc_size;
244
 
245
        struct allocated_block_header *ret = free_block;
246
        ret->size = alloc_size;
247
        alloc_block_set_boundary_tags(ret);
248
 
249
        free_block_insert(block);
250
        return ret;
251
    } else {
252
        // If there is not enough space for a free block header, simply
253
        //  convert to an allocated block header and return
254
        struct allocated_block_header *ret = free_block;
255
        alloc_block_set_boundary_tags(ret);
256
        return ret;
257
    }
258
}
259
 
260
/* 
261
 * free_block_search - Given a list of free_block_headers, finds
262
 *      and returns the first block that can hold the requested size. 
263
 *      If no blocks were found, returns NULL.
264
 */
265
void *free_block_search(void *free_block_list, int requested_size) {
266
    struct list *block_list = free_block_list;
267
    struct list_elem *e = list_begin(block_list);
268
    while (e != list_end(block_list)) {
269
        struct free_block_header *entry= list_entry(e, struct free_block_header, elem);
270
        if (entry->size >= requested_size) {
271
            return entry;
272
        } 
273
        e = list_next(e);
274
    }
275
 
276
    return NULL;
277
}
278
 
279
/*
280
 * mm_check - Checks the heap for consistency. Returns a non-zero value
281
 *      only if the heap is consistent.
282
 */
283
 int mm_check(void)
284
 {
285
    // Prints out the free blocks in each bucket when called
286
    printf("Heap allocated from %d to %d of size %d\n", (int)mem_heap_lo(), (int)mem_heap_hi(), mem_heapsize());
287
    printf("----------------------------------------\n");
288
    struct list_elem *index = list_begin(&index_of_free_lists);
289
    while (index != list_end(&index_of_free_lists)) {
290
        struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
291
        printf("Bucket of size %d - %d:\n", index_entry->size_min, index_entry->size_max);
292
 
293
        struct list_elem *entry = list_begin(&index_entry->free_block_list);
294
        while(entry != list_end(&index_entry->free_block_list)) {
295
            struct free_block_header *block = list_entry(entry, struct free_block_header, elem);
296
            printf("\tFree Block at %d of size %d\n", (int)block, block->size);
297
            entry = list_next(entry);
298
        }
299
 
300
        index = list_next(index);
301
    }
302
    printf("----------------------------------------\n");
303
    return 1;
304
 }
305
 
306
/* 
307
 * mm_init - Initialize the malloc package.
308
 */
309
int mm_init(void)
310
{
311
    /* Sanity checks. */
312
    assert((ALIGNMENT & (ALIGNMENT - 1)) == 0); // power of 2
313
    assert(sizeof(struct allocated_block_header) == ALIGNMENT);
314
    assert(offsetof(struct allocated_block_header, size) == 0);
315
    assert(offsetof(struct allocated_block_header, payload) % ALIGNMENT == 0);
316
 
317
    // Initialize the overall list of free lists
318
    list_init(&index_of_free_lists);
319
 
320
    // Initialize the list for each bucket size
321
    list_init(&bucket_1.free_block_list);
322
    list_init(&bucket_2.free_block_list);
323
    list_init(&bucket_3.free_block_list);
324
    list_init(&bucket_4.free_block_list);
325
    list_init(&bucket_5.free_block_list);
326
    list_init(&bucket_6.free_block_list);
327
    list_init(&bucket_7.free_block_list);
328
    list_init(&bucket_8.free_block_list);
329
    list_init(&bucket_9.free_block_list);
330
    list_init(&bucket_10.free_block_list);
331
    list_init(&bucket_11.free_block_list);
332
    list_init(&bucket_12.free_block_list);
333
    list_init(&bucket_13.free_block_list);
334
 
335
    // Insert the list into the main list
336
    list_push_back(&index_of_free_lists, &bucket_1.elem);
337
    list_push_back(&index_of_free_lists, &bucket_2.elem);
338
    list_push_back(&index_of_free_lists, &bucket_3.elem);
339
    list_push_back(&index_of_free_lists, &bucket_4.elem);
340
    list_push_back(&index_of_free_lists, &bucket_5.elem);
341
    list_push_back(&index_of_free_lists, &bucket_6.elem);
342
    list_push_back(&index_of_free_lists, &bucket_7.elem);
343
    list_push_back(&index_of_free_lists, &bucket_8.elem);
344
    list_push_back(&index_of_free_lists, &bucket_9.elem);
345
    list_push_back(&index_of_free_lists, &bucket_10.elem);
346
    list_push_back(&index_of_free_lists, &bucket_11.elem);
347
    list_push_back(&index_of_free_lists, &bucket_12.elem);
348
    list_push_back(&index_of_free_lists, &bucket_13.elem);
349
 
350
    return 0;
351
}
352
 
353
/* 
354
 * mm_malloc - Allocate a block by incrementing the brk pointer.
355
 *     Always allocate a block whose size is a multiple of the alignment.
356
 */
357
void *mm_malloc(size_t size)
358
{
359
    int newsize = roundup(size + sizeof(struct free_block_header));
360
    int searchsize = newsize;
361
 
362
    // Look for free block list that contains the right sized blocks
363
    struct list_elem *index = list_begin(&index_of_free_lists);
364
    while (index != list_end(&index_of_free_lists)) {
365
        struct free_list_index *index_entry = list_entry(index, struct free_list_index, elem);
366
 
367
        if (searchsize >= index_entry->size_min && searchsize <= index_entry->size_max) {
368
            // Try to get a free block that is large enough to hold the requested size
369
            struct free_block_header *free_block = free_block_search(&index_entry->free_block_list, newsize);
370
            if (free_block == NULL) {
371
                // List is empty, look at the next block size
372
                searchsize = index_entry->size_max + 1;
373
            } else {
374
                // Empty block was found, use the block to allocate space
375
                struct allocated_block_header *alloc_block = free_block_alloc(free_block, newsize, true);
376
                return alloc_block->payload;
377
            }
378
        }
379
        index = list_next(index);
380
    }
381
 
382
 
383
    // If no blocks were found, expand the heap and allocate the block from there
384
 
385
    // Calculage how many pages are needed to fit the block
386
    int alloc_size = (newsize / mem_pagesize() + 1) * mem_pagesize();
387
 
388
    // Get a number of pages from the heap and allocate from there
389
    struct free_block_header *free_block = mem_sbrk(alloc_size);
390
    if (free_block == NULL)
391
        return NULL;
392
    free_block->size = alloc_size;
393
    struct allocated_block_header *alloc_block = free_block_alloc(free_block, newsize, false);
394
    return alloc_block->payload;
395
}
396
 
397
/*
398
 * mm_free - Frees the specified block, inserting it into the list of free blocks
399
 */
400
void mm_free(void *ptr)
401
{
402
    // Get a pointer to the original block
403
    struct free_block_header *new_block = ptr - offsetof(struct allocated_block_header, payload);
404
 
405
    // Mark the block as free and insert it into the list of free blocks
406
    new_block->size ^= 1;   
407
    free_block_insert(new_block);
408
}
409
 
410
/*
411
 * mm_realloc - Resizes the specified block, leaving it if the size is smaller and allocating
412
 *      and copying the data to a new block if the size is larger. Extra space is left for
413
 *      future resizing.
414
 */
415
void *mm_realloc(void *oldptr, size_t size)
416
{
417
    struct allocated_block_header *old_block = oldptr - offsetof(struct allocated_block_header, payload);
418
    // old_block->size ^= 1;
419
 
420
    // If the block is already large enough to hold the new size, simply return
421
    if (size <= old_block->size - sizeof(struct free_block_header) - sizeof(size_t))
422
        return old_block->payload;
423
 
424
    // // Otherwise we need to allocate a new block of the request size
425
    void *new_block = mm_malloc(size + 6 * mem_pagesize());
426
    if (new_block == NULL)
427
        return NULL;
428
 
429
    // Copy the old data to the new block
430
    size_t copySize = old_block->size;
431
    if (size < copySize)
432
        copySize = size;
433
 
434
    memcpy(new_block, oldptr, copySize);
435
 
436
    // Free the old block and return the new one
437
    mm_free(oldptr);
438
    return new_block;
439
}
440