diff --git a/.gitignore b/.gitignore index 5645580..3ae6fe7 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,5 @@ include/config.h* missing stamp-h1 .deps -tests/test_bitmapalloc \ No newline at end of file +tests/test_bitmapalloc +tests/test_buddyalloc \ No newline at end of file diff --git a/include/Makefile.am b/include/Makefile.am index d300281..9d6bc93 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -1 +1,2 @@ -nobase_include_HEADERS = libmalloc/bitmap_alloc.h libmalloc/memmap.h libmalloc/common.h \ No newline at end of file +nobase_include_HEADERS = libmalloc/bitmap_alloc.h libmalloc/buddy_alloc.h \ + libmalloc/memmap.h libmalloc/common.h \ No newline at end of file diff --git a/include/libmalloc/bitmap_alloc.h b/include/libmalloc/bitmap_alloc.h index 665bd49..6f6f606 100644 --- a/include/libmalloc/bitmap_alloc.h +++ b/include/libmalloc/bitmap_alloc.h @@ -1,5 +1,5 @@ -#ifndef _LIBMALLOC_BUDDYALLOC_H -#define _LIBMALLOC_BUDDYALLOC_H +#ifndef _LIBMALLOC_BITMAPALLOC_H +#define _LIBMALLOC_BITMAPALLOC_H #include "memmap.h" #include "common.h" diff --git a/include/libmalloc/buddy_alloc.h b/include/libmalloc/buddy_alloc.h new file mode 100644 index 0000000..c913b78 --- /dev/null +++ b/include/libmalloc/buddy_alloc.h @@ -0,0 +1,55 @@ +#ifndef _LIBMALLOC_BUDDYALLOC_H +#define _LIBMALLOC_BUDDYALLOC_H + +#include "memmap.h" +#include "common.h" + +typedef struct buddy_block_t +{ + struct buddy_block_t *linkb; + + struct buddy_block_t *linkf; + + unsigned long kval; + + unsigned long tag; + +} buddy_block_t; + +typedef struct buddy_descriptor_t +{ + /** + * @brief An array of `buddy_block_t` structs serving as the heads of the + * lists of available blocks. avail[k] serves as the head of the list of + * blocks of size 2^k. + */ + buddy_block_t *avail; + + buddy_block_t *block_map; + + unsigned long block_map_size; + + unsigned long max_kval; + + unsigned long block_size; + + unsigned long offset; + + unsigned long free_block_count; + + int (*mmap)(void *location, unsigned long size); + +} buddy_descriptor_t; + +unsigned long buddy_map_size(const memory_map_t *map, unsigned long block_size); + +unsigned long buddy_reserve(buddy_descriptor_t *heap, unsigned long size); + +void buddy_free(buddy_descriptor_t *heap, unsigned long location); + +void buddy_free_size(buddy_descriptor_t *heap, unsigned long size, + unsigned long location); + +int buddy_alloc_init(buddy_descriptor_t *heap, memory_map_t *map); + +#endif \ No newline at end of file diff --git a/src/Makefile.am b/src/Makefile.am index c4f539b..43ed361 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -1,4 +1,4 @@ lib_LIBRARIES = libmalloc.a -libmalloc_a_SOURCES = bitmap_alloc.c memmap.c +libmalloc_a_SOURCES = buddy_alloc.c bitmap_alloc.c memmap.c libmalloc_a_CFLAGS = -I$(prefix)/include \ No newline at end of file diff --git a/src/buddy_alloc.c b/src/buddy_alloc.c new file mode 100644 index 0000000..2b62205 --- /dev/null +++ b/src/buddy_alloc.c @@ -0,0 +1,161 @@ +#include "libmalloc/buddy_alloc.h" +#include "util.h" + +#define BLOCK_RESERVED 0 +#define BLOCK_FREE 1 + +static unsigned long compute_memory_size(const memory_map_t *map) +{ + // Find the last available region in the memory map. + int map_index = map->size - 1; + while(map->array[map_index].type != M_AVAILABLE) + { + map_index--; + } + + return map->array[map_index].location + map->array[map_index].size; +} + +static void insert_block(buddy_descriptor_t *heap, unsigned long index, + unsigned long k) +{ + heap->free_block_count += 1UL << k; + while(k < heap->max_kval) + { + unsigned long buddy_index = index ^ (1UL << k); + if(heap->block_map[buddy_index].tag != BLOCK_FREE + || heap->block_map[buddy_index].kval != k) + { + break; + } + heap->block_map[buddy_index].linkb->linkf = heap->block_map[buddy_index].linkf; + heap->block_map[buddy_index].linkf->linkb = heap->block_map[buddy_index].linkb; + heap->block_map[buddy_index].tag = BLOCK_RESERVED; + k++; + if(buddy_index < index) + { + index = buddy_index; + } + } + heap->block_map[index].tag = BLOCK_FREE; + buddy_block_t *p = heap->avail[k].linkf; + heap->block_map[index].linkf = p; + heap->block_map[index].linkb = &heap->avail[k]; + p->linkb = &heap->block_map[index]; + heap->avail[k].linkf = &heap->block_map[index]; + heap->block_map[index].kval = k; +} + +unsigned long buddy_map_size(const memory_map_t *map, unsigned long block_size) +{ + unsigned long memory_size = compute_memory_size(map); + return sizeof(buddy_block_t) * memory_size / block_size; +} + +unsigned long buddy_reserve(buddy_descriptor_t *heap, unsigned long size) +{ + unsigned long k = llog2((size - 1) / heap->block_size + 1); + for(unsigned long j = k; j <= heap->max_kval; j++) + { + if(heap->avail[j].linkf != &heap->avail[j]) + { + buddy_block_t *block = heap->avail[j].linkb; + heap->avail[j].linkb = block->linkb; + heap->avail[j].linkb->linkf = &heap->avail[j]; + block->tag = BLOCK_RESERVED; + while(j > k) + { + j--; + buddy_block_t *buddy = block + (1UL << j); + buddy->tag = BLOCK_FREE; + buddy->kval = j; + block->kval = j; + buddy->linkb = &heap->avail[j]; + buddy->linkf = &heap->avail[j]; + heap->avail[j].linkb = buddy; + heap->avail[j].linkf = buddy; + } + unsigned long index = block - heap->block_map; + heap->free_block_count -= 1UL << k; + return (unsigned long)heap->offset + index * heap->block_size; + } + } + return NOMEM; +} + +void buddy_free(buddy_descriptor_t *heap, unsigned long location) +{ + unsigned long index = (location - (unsigned long)heap->offset) / heap->block_size; + unsigned long k = llog2((heap->block_size * (1UL << heap->block_map[index].kval)) / heap->block_size); + insert_block(heap, index, k); +} + +void buddy_free_size(buddy_descriptor_t *heap, unsigned long location, + unsigned long size) +{ + unsigned long index = (location - (unsigned long)heap->offset) / heap->block_size; + unsigned long k = llog2(size / heap->block_size); + insert_block(heap, index, k); +} + +int buddy_alloc_init(buddy_descriptor_t *heap, memory_map_t *map) +{ + heap->block_map_size = buddy_map_size(map, heap->block_size); + heap->max_kval = llog2(heap->block_map_size / sizeof(buddy_block_t)); + heap->free_block_count = 0; + for(int i = 0; i <= heap->max_kval; i++) + { + heap->avail[i].linkf = &heap->avail[i]; + heap->avail[i].linkb = &heap->avail[i]; + } + + if(heap->block_map == (buddy_block_t*)0) + { + int map_index = 0; + while(map->array[map_index].type != M_AVAILABLE + || map->array[map_index].size < heap->block_map_size) + { + map_index++; + if(map_index >= map->size) + { + return -1; + } + } + + heap->block_map = (buddy_block_t*)(heap->offset + map->array[map_index].location); + memmap_insert_region(map, map->array[map_index].location, heap->block_map_size, M_UNAVAILABLE); + if(heap->mmap && heap->mmap(heap->block_map, heap->block_map_size)) + { + return -1; + } + } + + for(int i = 0; i < heap->block_map_size / sizeof(buddy_block_t); i++) + { + heap->block_map[i].tag = BLOCK_RESERVED; + heap->block_map[i].kval = 0; + heap->block_map[i].linkf = 0; + heap->block_map[i].linkb = 0; + } + + for(int i = 0; i < map->size; i++) + { + if(map->array[i].type != M_AVAILABLE) + { + continue; + } + + unsigned long location = (map->array[i].location + heap->block_size - 1); + location -= location % heap->block_size; + unsigned long region_end = map->array[i].location + map->array[i].size; + while(location + heap->block_size <= region_end) + { + unsigned long index = location / heap->block_size; + unsigned long k = 0; + insert_block(heap, index, k); + location += heap->block_size; + heap->free_block_count++; + } + } + return 0; +} diff --git a/tests/Makefile.am b/tests/Makefile.am index fe2ee89..492e40c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,6 +1,9 @@ if BUILD_TESTS - noinst_PROGRAMS = test_bitmapalloc + noinst_PROGRAMS = test_bitmapalloc test_buddyalloc test_bitmapalloc_SOURCES = test_bitmapalloc.c test_bitmapalloc_LDADD = ../src/libmalloc.a + + test_buddyalloc_SOURCES = test_buddyalloc.c + test_buddyalloc_LDADD = ../src/libmalloc.a endif \ No newline at end of file diff --git a/tests/test_buddyalloc.c b/tests/test_buddyalloc.c new file mode 100644 index 0000000..1b50f38 --- /dev/null +++ b/tests/test_buddyalloc.c @@ -0,0 +1,102 @@ +#include "libmalloc/buddy_alloc.h" +#include +#include + +typedef struct memblock_t +{ + unsigned long size; + unsigned long location; +} memblock_t; + +void print_heap(FILE *file, buddy_descriptor_t *heap) +{ + unsigned long count = 1UL << heap->max_kval; + fprintf(file, "heap {\n"); + fprintf(file, "\t.block_size = %lu\n", heap->block_size); + fprintf(file, "\t.max_kval = %lu\n", heap->max_kval); + fprintf(file, "\t.offset = %lu\n", heap->offset); + fprintf(file, "\t.block_map = {\n"); + for(unsigned int i = 0; i < count; i++) + { + unsigned long linkf_diff = heap->block_map[i].linkf - heap->block_map; + unsigned long linkb_diff = heap->block_map[i].linkb - heap->block_map; + fprintf(file, "\t\t%u: {.tag = %s, .kval = %lu, .linkf = %li, .linkb = %li}\n", + i, + heap->block_map[i].tag ? "BLOCK_FREE" : "BLOCK_RESERVED", + heap->block_map[i].kval, + linkf_diff < count ? linkf_diff : -1, + linkb_diff < count ? linkb_diff : -1); + } + fprintf(file, "\t}\n"); + fprintf(file, "\t.avail = {\n"); + for(unsigned int i = 0; i <= heap->max_kval; i++) + { + fprintf(file, "\t\t%u: {.linkf = %lu, .linkb = %lu}\n", + i, + heap->avail[i].linkf - heap->block_map, + heap->avail[i].linkb - heap->block_map); + } + fprintf(file, "\t}\n}\n"); +} + +int main(int argc, char **argv) +{ + unsigned long mem_size; + unsigned long block_size; + sscanf(argv[1], "%lu", &mem_size); + sscanf(argv[2], "%lu", &block_size); + + FILE *out = fopen("debug_output.txt", "w"); + if(out == NULL) + { + fprintf(stderr, "Failed to open debug output.\n"); + return 0; + } + + memory_map_t memory_map = { + .array = malloc(sizeof(memory_region_t) * 16), + .capacity = 16, + .size = 0 + }; + + buddy_descriptor_t heap = { + .avail = malloc(sizeof(buddy_block_t) * 20), + .block_map = malloc(buddy_map_size(&memory_map, block_size)), + .block_size = block_size, + .mmap = NULL, + .offset = 0 + }; + + memmap_insert_region(&memory_map, 0, mem_size, M_AVAILABLE); + if(buddy_alloc_init(&heap, &memory_map)) + { + fprintf(stderr, "Failed to initialize buddy allocator.\n"); + return 0; + } + + print_heap(out, &heap); + + memblock_t *blocks = calloc(mem_size / block_size, sizeof(memblock_t)); + + for(int i = 0; i < 1024; i++) + { + int index = rand() % (mem_size / block_size); + if(blocks[index].size == 0) + { + blocks[index].size = 1; + blocks[index].location = buddy_reserve(&heap, 1); + fprintf(out, "RESERVED %lu\n", blocks[index].location / heap.block_size); + } + else + { + buddy_free(&heap, blocks[index].location); + blocks[index].size = 0; + fprintf(out, "FREED %lu\n", blocks[index].location / heap.block_size); + } + print_heap(out, &heap); + } + + print_heap(out, &heap); + + return 0; +} \ No newline at end of file