Add comments
This commit is contained in:
@@ -1,6 +1,42 @@
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
/* === INTRODUCTION ===
|
||||
* This is the implementation of a general purpose allocator that uses
|
||||
* the "buddy system". It uses a pool of memory specified by the user
|
||||
* and allows allocations up to a specified threshold.
|
||||
*
|
||||
* === THE BUDDY SYSTEM ===
|
||||
* The buddy system is an allocator that puts memory regions available
|
||||
* for allocation in buckets based on their size. Each bucket contains
|
||||
* regions of a different power of 2. When the user request the allocation
|
||||
* of a region of a given length, the allocator looks for an unused
|
||||
* region from the appropriate bucket (the one containing the smallest
|
||||
* regions that aren't smaller of the requested size) and returns it.
|
||||
* If the bucket is empty, the allocator gets one from the list of larger
|
||||
* blocks and splits it. One half is returned to the user and the other
|
||||
* is put in the bucket. These two blocks that were split from one larger
|
||||
* block are called "buddies". When deallocating a block, the allocator
|
||||
* checks if its "buddy" is currently used. If it's not, it merges the
|
||||
* buddies and puts the larger block in the bucket. If the buddy is used,
|
||||
* only the region provided by the user is put in the bucket. This mechanism
|
||||
* is recursive, so if two buddies of size N can be merged, the allocator
|
||||
* now looks for the buddy of size 2N and so on until either a buddy is
|
||||
* in use or it got to the largest block possible. The same goes for the
|
||||
* allocation code.
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* This is the minimum and maximum block size. The allocator uses free
|
||||
* lists to keep track of unused blocks, so a block must be at least
|
||||
* the size of a pointer. We assume a pointer is 8 bytes long, so the
|
||||
* minimum value must be greater or equal to 3 (log2(8) = 3).
|
||||
*
|
||||
* For the maximum value there is really no downside in making it big,
|
||||
* except for the fact that the pool provided by the user should at
|
||||
* least be that big.
|
||||
*/
|
||||
#define BUDDY_ALLOC_MAX_BLOCK_LOG2 13
|
||||
#define BUDDY_ALLOC_MIN_BLOCK_LOG2 3
|
||||
|
||||
@@ -8,21 +44,23 @@ _Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 <= BUDDY_ALLOC_MAX_BLOCK_LOG2);
|
||||
_Static_assert(BUDDY_ALLOC_MIN_BLOCK_LOG2 > 2);
|
||||
|
||||
#define BUDDY_ALLOC_NUM_LISTS (BUDDY_ALLOC_MAX_BLOCK_LOG2 - BUDDY_ALLOC_MIN_BLOCK_LOG2 + 1)
|
||||
|
||||
// To keep track of the allocation state of a page,
|
||||
// we need one bit for each possible block that can
|
||||
// be made out of it. For instance, if the page can
|
||||
// only be allocated in its entirety, 1 bit is required.
|
||||
// If the blocks halfs can be allocated too, 3 bits
|
||||
// are required: 1 for the page, 1 for the frist half
|
||||
// and 1 for the second half. Allowing the allocation
|
||||
// of page quarters requires 4 more bits, for a total
|
||||
// of 7. In general, if we allow splitting a page N
|
||||
// times (N=0 means only the entire page can be allocated),
|
||||
// then 2^(N+1)-1 bits are necessary.
|
||||
#define BUDDY_ALLOC_MAX_BLOCK_SIZE (1U << BUDDY_ALLOC_MAX_BLOCK_LOG2)
|
||||
#define BUDDY_ALLOC_MIN_BLOCK_SIZE (1U << BUDDY_ALLOC_MIN_BLOCK_LOG2)
|
||||
/*
|
||||
* To keep track of the allocation state of a page,
|
||||
* we need one bit for each possible block that can
|
||||
* be made out of it. For instance, if the page can
|
||||
* only be allocated in its entirety, 1 bit is required.
|
||||
* If the blocks halfs can be allocated too, 3 bits
|
||||
* are required: 1 for the page, 1 for the frist half
|
||||
* and 1 for the second half. Allowing the allocation
|
||||
* of page quarters requires 4 more bits, for a total
|
||||
* of 7. In general, if we allow splitting a page N
|
||||
* times (N=0 means only the entire page can be allocated),
|
||||
* then 2^(N+1)-1 bits are necessary.
|
||||
*/
|
||||
#define BUDDY_ALLOC_BITS_PER_PAGE ((1U << (BUDDY_ALLOC_NUM_LISTS)) - 1)
|
||||
#define BUDDY_ALLOC_WORDS_PER_PAGE ((BUDDY_ALLOC_BITS_PER_PAGE + 31) / 32)
|
||||
|
||||
struct page_info {
|
||||
uint32_t bits[BUDDY_ALLOC_WORDS_PER_PAGE];
|
||||
};
|
||||
@@ -34,7 +72,37 @@ struct buddy_alloc {
|
||||
int num_info;
|
||||
};
|
||||
|
||||
/*
|
||||
* Initialize the allocator.
|
||||
*
|
||||
* The allocator will use as allocation memory the [size]
|
||||
* bytes as position [base]. If the memory pool isn't
|
||||
* aligned to BUDDY_ALLOC_MAX_BLOCK_SIZE, the first bytes
|
||||
* are discarded.
|
||||
*
|
||||
* The user needs to provide the allocator with an array
|
||||
* of [struct page_info] with a capacity equal to the number
|
||||
* of (aligned) BUDDY_ALLOC_MAX_BLOCK_SIZE blocks in the
|
||||
* pool. If less page_info structs are provided than necessary
|
||||
* for the pool, the exceeding portion of the pool is
|
||||
* discarded.
|
||||
*/
|
||||
struct buddy_alloc buddy_startup(char *base, size_t size, struct page_info *page_info, int num_page_info);
|
||||
void buddy_cleanup(struct buddy_alloc *alloc);
|
||||
void *buddy_malloc (struct buddy_alloc *alloc, size_t len);
|
||||
void buddy_free (struct buddy_alloc *alloc, size_t len, void *ptr);
|
||||
|
||||
/*
|
||||
* Deinitialize the allocator.
|
||||
*/
|
||||
void buddy_cleanup(struct buddy_alloc *alloc);
|
||||
|
||||
/*
|
||||
* Allocate a memory region of size [len]. If allocation
|
||||
* fails, NULL is returned.
|
||||
*/
|
||||
void *buddy_malloc(struct buddy_alloc *alloc, size_t len);
|
||||
|
||||
/*
|
||||
* Deallocate a memory region allocated using [buddy_malloc].
|
||||
* The [len] argument must be the same value passed when
|
||||
* allocating.
|
||||
*/
|
||||
void buddy_free(struct buddy_alloc *alloc, size_t len, void *ptr);
|
||||
|
||||
Reference in New Issue
Block a user