final ver.

This commit is contained in:
Felicia Seo 2022-11-14 20:19:30 -05:00
parent b48ada3663
commit 20ae0f9ec2

301
mm.c
View File

@ -1,38 +1,48 @@
/* /*
* mm.c - Dynamic Memory Allocator * mm.c - Dynamic Memory Allocator
* *
* This is a segregated free list implementation with multiple * This is an external, segregated free list implementation with multiple lists that hold blocks of
* lists to hold different block sizes. The size ranges for our 8 lists * are powers of 2, with * different size ranges. The size ranges for our 8 lists are determined by powers of 2.
* the smallest size on the lower bound being 8 words, * - 1st list holds blocks that are 8 * 1 = 8 words.
* - 2nd list holds blocks that are 8 * 2 = 16 words. (the constant 8 is the minimum block size)
* - 3rd list holds blocks that are 8 * 4 = 32 words.
* (and so on)
* *
* A seperate list is used to store the sizes, and is checked when a * When a free block is inserted to a list, it's placed in the front, and when a free block is
* free block is added to a list. * removed from a list, it's taken from the back. In other words, last in, first out (LIFO) was
* implemented.
* *
* Both allocated and free blocks are stored in the heap * The lists are stored in an array (free_lists) based on their max size.
* with the pointer heap_listp.
* Each block has a header and a footer, which
* store the size in 31 bits, and 1 bit for determining whether it is
* free.
* *
* Blocks have a payload which holds hold necessary data and a list_elem struct so blocks can be stored in lists * An array is used to store the max sizes (list_sizes), and is checked when a free block is added
* to a list.
*
* Both allocated and free blocks use the same structure (block) and are stored in the heap with the
* pointer heap_listp.
* - Each block has a header and a footer, which store the size in 31 bits, and 1 bit for determining
* whether it is free.
* - Blocks have a payload which holds hold necessary data.
* - They also have a list_elem struct so blocks can be stored in lists.
*/ */
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <assert.h> #include <assert.h>
#include <stddef.h>
#include "mm.h" #include "mm.h"
#include "memlib.h" #include "memlib.h"
#include "list.h" #include "list.h"
#include "config.h" #include "config.h"
struct boundary_tag { struct boundary_tag {
int inuse:1; int inuse:1;
int size:31; int size:31;
}; };
/* FENCE is used for heap prologue/epilogue. */ /*
* FENCE is used for heap prologue/epilogue.
*/
const struct boundary_tag FENCE = { const struct boundary_tag FENCE = {
.inuse = 1, .inuse = 1,
.size = 0 .size = 0
@ -44,184 +54,204 @@ struct block {
struct list_elem elem; struct list_elem elem;
}; };
static struct block *heap_listp = 0; /*
* Basic constants and macros:
/* Basic constants and macros */ */
#define WSIZE sizeof(struct boundary_tag) /* Word and header/footer size (bytes) */ #define WSIZE sizeof(struct boundary_tag) // Word and header/footer size (in bytes)
#define DSIZE 2*WSIZE #define DSIZE 2*WSIZE
#define MIN_BLOCK_SIZE_WORDS 8 /* Minimum block size in words */ #define MIN_BLOCK_SIZE_WORDS 8 // Minimum block size (in words)
#define CHUNKSIZE (1<<10) /* Extend heap by this amount (words) */ #define CHUNKSIZE (1<<10) // Extend heap by this amount (in words)
#define NUM_LISTS 8 // Number of free lists
#define NUM_LISTS 8 /* Number of free lists */
static inline size_t max(size_t x, size_t y) { static inline size_t max(size_t x, size_t y) {
return x > y ? x : y; return x > y ? x : y;
} }
static size_t align(size_t size) { static size_t align(size_t size) {
return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1); return (size + ALIGNMENT - 1) & ~(ALIGNMENT - 1);
} }
/*
* Global variables:
*/
static struct block *heap_listp = 0;
static struct list free_lists[NUM_LISTS]; static struct list free_lists[NUM_LISTS];
static size_t list_sizes[NUM_LISTS]; static size_t list_sizes[NUM_LISTS];
/* Function prototypes for internal helper routines */ /*
* Function prototypes for internal helper routines:
*/
static struct block *extend_heap(size_t words); static struct block *extend_heap(size_t words);
static void place(struct block *blck, size_t asize); static void place(struct block *blck, size_t asize);
static struct list* find_list(int not_empty, size_t size); static struct list* find_list(int not_empty, size_t size);
static struct block *find_fit(size_t num_words); static struct block *find_fit(size_t num_words);
static struct block *coalesce(struct block *bp); static struct block *coalesce(struct block *bp);
/* Given a block, obtain previous's block footer. /*
Works for left-most block also. */ * Given a block, obtains previous's block footer. Works for left-most block also.
*/
static struct boundary_tag * prev_blk_footer(struct block *blk) { static struct boundary_tag * prev_blk_footer(struct block *blk) {
return &blk->header - 1; return &blk->header - 1;
} }
/* Return if block is free */ /*
* Returns if block is free.
*/
static bool blk_free(struct block *blk) { static bool blk_free(struct block *blk) {
return !blk->header.inuse; return !blk->header.inuse;
} }
/* Return size of block is free */ /*
* Returns size of block.
*/
static size_t blk_size(struct block *blk) { static size_t blk_size(struct block *blk) {
return blk->header.size; return blk->header.size;
} }
/* Given a block, obtain pointer to previous block. /*
Not meaningful for left-most block. */ * Given a block, obtains pointer to previous block. Not meaningful for left-most block.
*/
static struct block *prev_blk(struct block *blk) { static struct block *prev_blk(struct block *blk) {
struct boundary_tag *prevfooter = prev_blk_footer(blk); struct boundary_tag *prevfooter = prev_blk_footer(blk);
assert(prevfooter->size != 0); assert(prevfooter->size != 0);
return (struct block *)((void *)blk - WSIZE * prevfooter->size); return (struct block *)((void *)blk - WSIZE * prevfooter->size);
} }
/* Given a block, obtain pointer to next block. /*
Not meaningful for right-most block. */ * Given a block, obtains pointer to next block. Not meaningful for right-most block.
*/
static struct block *next_blk(struct block *blk) { static struct block *next_blk(struct block *blk) {
assert(blk_size(blk) != 0); assert(blk_size(blk) != 0);
return (struct block *)((void *)blk + WSIZE * blk->header.size); return (struct block *)((void *)blk + WSIZE * blk->header.size);
} }
/* Given a block, obtain its footer boundary tag */ /*
* Given a block, obtain its footer boundary tag.
*/
static struct boundary_tag * get_footer(struct block *blk) { static struct boundary_tag * get_footer(struct block *blk) {
return ((void *)blk + WSIZE * blk->header.size) return ((void *)blk + WSIZE * blk->header.size)
- sizeof(struct boundary_tag); - sizeof(struct boundary_tag);
} }
/* Set a block's size and inuse bit in header and footer */ /*
* Sets a block's size and inuse bit in header and footer.
*/
static void set_header_and_footer(struct block *blk, int size, int inuse) { static void set_header_and_footer(struct block *blk, int size, int inuse) {
blk->header.inuse = inuse; blk->header.inuse = inuse;
blk->header.size = size; blk->header.size = size;
* get_footer(blk) = blk->header; /* Copy header to footer */ *get_footer(blk) = blk->header; // copies header to footer
} }
/* Mark a block as used and set its size. */ /*
* Sets a block as used and sets its size.
*/
static void set_block_used(struct block *blk, int size) { static void set_block_used(struct block *blk, int size) {
set_header_and_footer(blk, size, 1); set_header_and_footer(blk, size, 1);
} }
/*
/* Mark a block as free and set its size. */ * Sets a block as free and sets its size. Adds the block to a free list.
*/
static void set_block_free(struct block *blk, int size) { static void set_block_free(struct block *blk, int size) {
set_header_and_footer(blk, size, 0); set_header_and_footer(blk, size, 0);
list_push_front(find_list(0, size), &blk->elem); list_push_front(find_list(0, size), &blk->elem); // (LIFO)
} }
/*
* Initializes the memory manager.
*/
int mm_init(void) int mm_init(void)
{ {
assert (offsetof(struct block, payload) == 4); assert (offsetof(struct block, payload) == 4);
assert (sizeof(struct boundary_tag) == 4); assert (sizeof(struct boundary_tag) == 4);
/* Create the initial empty heap */ // Creates the initial empty heap.
struct boundary_tag * initial = mem_sbrk(4 * sizeof(struct boundary_tag)); struct boundary_tag * initial = mem_sbrk(4 * sizeof(struct boundary_tag));
if (initial == NULL) if (initial == NULL)
return -1; return -1;
/* We use a slightly different strategy than suggested in the book. /* We use a slightly different strategy than suggested in the book. Rather than placing a
* Rather than placing a min-sized prologue block at the beginning * min-sized prologue block at the beginning of the heap, we simply place two fences. The
* of the heap, we simply place two fences. * consequence is that coalesce() must call prev_blk_footer() and not prev_blk() because
* The consequence is that coalesce() must call prev_blk_footer() * prev_blk() cannot be called on the left-most block.
* and not prev_blk() because prev_blk() cannot be called on the
* left-most block.
*/ */
initial[2] = FENCE; /* Prologue footer */ initial[2] = FENCE; // prologue header
heap_listp = (struct block *)&initial[3]; heap_listp = (struct block *)&initial[3];
initial[3] = FENCE; /* Epilogue header */ initial[3] = FENCE; // epilogue header
for (int i = 0; i < NUM_LISTS; i++) {
// Initializes the free_lists.
for (int i = 0; i < NUM_LISTS; i++)
list_init(&free_lists[i]); list_init(&free_lists[i]);
} // Initializes list_sizes (powers of 2).
for (int i = 0; i < NUM_LISTS; i++) { for (int i = 0; i < NUM_LISTS; i++) {
int size = 1; int size = 1;
for (int j = 0; j < i; j++) { for (int j = 0; j < i; j++)
size *= 2; size *= 2;
}
list_sizes[i] = MIN_BLOCK_SIZE_WORDS * (size); list_sizes[i] = MIN_BLOCK_SIZE_WORDS * (size);
} }
/* Extend the empty heap with a free block of CHUNKSIZE bytes */ // Extends the empty heap with a free block of CHUNKSIZE bytes.
if (extend_heap(CHUNKSIZE) == NULL) if (extend_heap(CHUNKSIZE) == NULL)
return -1; return -1;
return 0; return 0;
} }
/* /*
* The mm malloc routine returns a pointer to an allocated block payload of at * Returns a pointer to an allocated block with a payload of at least the given size in (bytes). The
* least size bytes. The entire allocated block should lie within the heap region and should * entire allocated block should lie within the heap region and should not overlap with other
* not overlap with any other allocated chunk. * allocated blocks.
*/ */
void *mm_malloc(size_t size) { void *mm_malloc(size_t size) {
struct block *blck; if (size == 0)
if (size == 0) {
return NULL; return NULL;
}
struct block *blck;
size_t words = max(MIN_BLOCK_SIZE_WORDS, align(size + DSIZE) / WSIZE); size_t words = max(MIN_BLOCK_SIZE_WORDS, align(size + DSIZE) / WSIZE);
/* Find fit for size */ // Attempts to find a block from the free lists that fit.
if ((blck = find_fit(words)) != NULL) { if ((blck = find_fit(words)) != NULL) {
place(blck, words); place(blck, words); // malloc successful
return blck->payload; return blck->payload;
} }
if ((blck = extend_heap(words)) == NULL){ // Attempts to extend the heap to accomodate.
if ((blck = extend_heap(words)) == NULL)
return NULL; return NULL;
}
place(blck, words); place(blck, words);
return blck->payload; return blck->payload; // malloc successful
} }
void mm_free(void *bp) /*
{ * Frees the block pointed by the given pointer.
assert (heap_listp != 0); // assert that mm_init was called */
void mm_free(void *bp) {
// Asserts that mm_init() was called.
assert (heap_listp != 0);
if (bp == 0) if (bp == 0)
return; return;
/* Find block from user pointer */ // Finds a block from the given pointer and frees it.
struct block *blk = bp - offsetof(struct block, payload); struct block *blk = bp - offsetof(struct block, payload);
size_t size = blk_size(blk); set_block_free(blk, blk_size(blk));
set_block_free(blk, size);
coalesce(blk); coalesce(blk);
} }
/*
* Frees the block pointed by the given pointer.
*/
void *mm_realloc(void *ptr, size_t size) { void *mm_realloc(void *ptr, size_t size) {
size_t bytesize; size_t bytesize;
void *new_ptr; void *new_ptr;
if (ptr == NULL) { // Equivalent to mallocing.
if (ptr == NULL)
return mm_malloc(size); return mm_malloc(size);
}
// Equivalent to freeing.
if (size == 0) { if (size == 0) {
mm_free(ptr); mm_free(ptr);
return NULL; return NULL;
@ -231,8 +261,8 @@ void *mm_realloc(void *ptr, size_t size) {
bool prev_alloc = prev_blk_footer(oldblock)->inuse; bool prev_alloc = prev_blk_footer(oldblock)->inuse;
bool next_alloc = !blk_free(next_blk(oldblock)); bool next_alloc = !blk_free(next_blk(oldblock));
if (!next_alloc) // CASE 1: The next block is free.
{ if (!next_alloc) {
struct block *right; struct block *right;
right = next_blk(oldblock); right = next_blk(oldblock);
list_remove(&right->elem); list_remove(&right->elem);
@ -240,11 +270,11 @@ void *mm_realloc(void *ptr, size_t size) {
} }
if (blk_size(oldblock) >= size) { if (blk_size(oldblock) >= size) {
return oldblock->payload; return oldblock->payload; // reallocing was successful
} }
if (!prev_alloc) // CASE 2: The previous block is free.
{ if (!prev_alloc) {
struct block *left; struct block *left;
left = prev_blk(oldblock); left = prev_blk(oldblock);
list_remove(&left->elem); list_remove(&left->elem);
@ -252,55 +282,53 @@ void *mm_realloc(void *ptr, size_t size) {
oldblock = left; oldblock = left;
} }
if (blk_size(oldblock) >= size) {
if (blk_size(oldblock) >= size)
{
bytesize = blk_size(oldblock) * WSIZE; bytesize = blk_size(oldblock) * WSIZE;
if(size < bytesize) bytesize = size; if(size < bytesize)
bytesize = size;
memcpy(oldblock->payload, ptr, bytesize); memcpy(oldblock->payload, ptr, bytesize);
return oldblock->payload; return oldblock->payload; // reallocing was successful
} }
// DEFAULT:
new_ptr = mm_malloc(size); new_ptr = mm_malloc(size);
if(!new_ptr)
return 0;
if(!new_ptr) return 0;
struct block *copyblk = ptr - offsetof(struct block, payload); struct block *copyblk = ptr - offsetof(struct block, payload);
bytesize = blk_size(copyblk) * WSIZE; //find number of BYTES, not words bytesize = blk_size(copyblk) * WSIZE;
if(size < bytesize) bytesize = size; if(size < bytesize)
bytesize = size;
memcpy(new_ptr, ptr, bytesize); memcpy(new_ptr, ptr, bytesize);
mm_free(oldblock->payload); mm_free(oldblock->payload);
return new_ptr; return new_ptr;
} }
/*
* Combines adjacent free blocks. Returns a pointer to the coalesced block.
*/
static struct block *coalesce(struct block *bp) static struct block *coalesce(struct block *bp)
{ {
bool prev_alloc = prev_blk_footer(bp)->inuse; /* is previous block allocated? */ int prev_alloc = prev_blk_footer(bp)->inuse; // previous block allocated?
bool next_alloc = ! blk_free(next_blk(bp)); /* is next block allocated? */ int next_alloc = !blk_free(next_blk(bp)); // next block allocated?
size_t size = blk_size(bp); size_t size = blk_size(bp);
if (prev_alloc && next_alloc) { /* Case 1 */ // CASE 1: Both are allocated, nothing to coalesce.
// both are allocated, nothing to coalesce if (prev_alloc && next_alloc)
return bp; return bp;
}
else if (prev_alloc && !next_alloc) { /* Case 2 */ // CASE 2: Next block is free, combine with next block.
// combine this block and next block by extending it else if (prev_alloc && !next_alloc) {
list_remove(&bp->elem); list_remove(&bp->elem);
list_remove(&next_blk(bp)->elem); list_remove(&next_blk(bp)->elem);
size_t new_size = size + blk_size(next_blk(bp)); size_t new_size = size + blk_size(next_blk(bp));
set_block_free(bp, new_size); set_block_free(bp, new_size);
} }
else if (!prev_alloc && next_alloc) { /* Case 3 */ // CASE 3: Previous block is free, combine with previous block.
// combine previous and this block by extending previous else if (!prev_alloc && next_alloc) {
list_remove(&prev_blk(bp)->elem); list_remove(&prev_blk(bp)->elem);
list_remove(&bp->elem); list_remove(&bp->elem);
size_t new_size = blk_size(prev_blk(bp)) + size; size_t new_size = blk_size(prev_blk(bp)) + size;
@ -308,8 +336,8 @@ static struct block *coalesce(struct block *bp)
set_block_free(bp, new_size); set_block_free(bp, new_size);
} }
else { /* Case 4 */ // CASE 4: Previous and next blocks are free, combine with both.
// combine all previous, this, and next block into one else if (!prev_alloc && !next_alloc) {
list_remove(&prev_blk(bp)->elem); list_remove(&prev_blk(bp)->elem);
list_remove(&bp->elem); list_remove(&bp->elem);
list_remove(&next_blk(bp)->elem); list_remove(&next_blk(bp)->elem);
@ -317,88 +345,89 @@ static struct block *coalesce(struct block *bp)
bp = prev_blk(bp); bp = prev_blk(bp);
set_block_free(bp, new_size); set_block_free(bp, new_size);
} }
return bp; return bp;
} }
/* /*
* Extend heap with free block and return its block pointer * Extends the heap with free block and return its block pointer.
* Does not add new block to free list if called by realloc
*/ */
static struct block *extend_heap(size_t words) static struct block *extend_heap(size_t words) {
{
void *blck = mem_sbrk(words * WSIZE); void *blck = mem_sbrk(words * WSIZE);
if (blck == NULL) if (blck == NULL)
return NULL; return NULL;
/* Initialize free block header, footer and epilogue header. */ /* Initializes a free block header, footer, and epilogue header. */
struct block * blk = blck - sizeof(FENCE); struct block * blk = blck - sizeof(FENCE);
set_block_free(blk, words); set_block_free(blk, words);
next_blk(blk)->header = FENCE; next_blk(blk)->header = FENCE;
return coalesce(blk); return coalesce(blk);
} }
/* /*
* Allocate block of memory at address blck * Allocates block of memory at address block.
*/ */
static void place(struct block *blck, size_t asize) { static void place(struct block *blck, size_t asize) {
size_t csize = blk_size(blck); // Removes the block since it's no longer free.
//~ print_lists();
list_remove(&blck->elem); list_remove(&blck->elem);
//~ print_lists();
size_t csize = blk_size(blck);
size_t remaining_size = csize - asize; size_t remaining_size = csize - asize;
// Determines if the block has extra space that can be freed.
if (remaining_size >= MIN_BLOCK_SIZE_WORDS) { if (remaining_size >= MIN_BLOCK_SIZE_WORDS) {
set_block_used(blck, asize); set_block_used(blck, asize);
blck = next_blk(blck); blck = next_blk(blck);
set_block_free(blck, remaining_size); set_block_free(blck, remaining_size);
//~ print_lists();
} }
else { else
set_block_used(blck, csize); set_block_used(blck, csize);
}
} }
/** /*
* Find free list with blocks of given size * Finds a free list that can fit a block with the given size (in words).
* *
* not_empty == 0, find "any" list * not_empty == 0, find "any" list
* not_empty == 1, find a not empty list * not_empty == 1, find a non-empty list
*/ */
static struct list* find_list(int not_empty, size_t n_words) { static struct list* find_list(int not_empty, size_t n_words) {
// Parses the segregated free lists.
for (int i = 0; i < NUM_LISTS; i++) { for (int i = 0; i < NUM_LISTS; i++) {
/*
* Attempts to return the current list if...
* a.) the given size is less than/equal to the current list's max size.
* b.) the current list is the last one.
*/
if ((n_words <= list_sizes[i]) || (i == (NUM_LISTS - 1))) { if ((n_words <= list_sizes[i]) || (i == (NUM_LISTS - 1))) {
if (not_empty && list_empty(&free_lists[i])) { // Continues searching if a non-empty list is needed and the current list is empty.
if (not_empty && list_empty(&free_lists[i]))
continue; continue;
}
return &free_lists[i]; return &free_lists[i];
} }
} }
return NULL; return NULL;
} }
/* /*
* Find a fit for a block with num_words * Finds a free block that fits the given size (in words).
*/ */
static struct block *find_fit(size_t num_words) static struct block *find_fit(size_t num_words) {
{ // Selects a free list that contains a free block that fits.
struct list* free_list = find_list(1, num_words); struct list* free_list = find_list(1, num_words);
if (free_list == NULL) { if (free_list == NULL)
return NULL; return NULL;
}
/* Search for block from selected free list*/ // Searches for a free block from the selected free list that fits. (LIFO)
for (struct list_elem* e = list_back(free_list); e != list_head(free_list); e = list_prev(e)) { for (struct list_elem* e = list_back(free_list); e != list_head(free_list); e = list_prev(e)) {
struct block* bp = list_entry(e, struct block, elem); struct block* bp = list_entry(e, struct block, elem);
if (num_words <= blk_size(bp)) { if (num_words <= blk_size(bp))
return bp; return bp;
} }
}
return NULL; return NULL;
} }