There are several possible implementations for a mmap-based malloc:
Sequential (first-fit, best-fit).
Idea: Use a linked list with the last chunk sized to the remaining size of your page.
struct chunk
{
size_t size;
struct chunk *next;
int is_free;
}
- To allocate
- Iterate your list for a suitable free chunk (optimizable)
- If nothing's found, resize the last chunk to the required size and create a free chunk to the remaining size.
- If you reach the end of the page, (the
size
is too small, and next
is NULL), simply mmap a new page (optimisable: generate a custom page if the size is abnormal ...)
- To free, even simpler: simply set
is_free
to 1. optionally, you can check if the next chunk is also free and merge both in a bigger free chunk (watch out for page borders).
Pros: Easy to implement, trivial to understand, simple to tweak.
Cons: not very efficient (iterate your whole list to find a block?), need lots of optimisation, hectic memory organization
Binary buddies (I love binary arithmetics and recursion)
Idea: Use powers-of-2 as size units:
struct chunk
{
size_t size;
int is_free;
}
the structure here does not need a next
as you'll see.
The principle is the following:
- You have a 4096-bytes page. that is (-16 for metadata) 4080 usable bytes
- To allocate a small block, simply split up the page in two 2048-bytes chunks, and split again the first half in 1028-bytes chunks... until you get a suitable usable space (minimum at 32-bytes (16 usable)).
- Every block, if it isn't a full page, has a buddy.
- You end up with a tree-like structure like this:

- to access your buddy, use a binary XOR between your pointer and your block size.
Implementation:
- Allocating a block of size
Size
- Get the required
Block_size = 2^k > size + sizeof(chunk)
- find the smallest free space in the tree that fits
block_size
- If it can get smaller, Split it, recursively.
- Freeing a block
- Setting
is_free
to 1
- checking if your buddy is free (XOR size, don't forget to verify he's the same size as you)
- if he is, double his size. Recurse.
Pros: Extremely fast and memory-efficient, clean.
Cons: Complicated, a few tricky cases (page borders and buddy sizes)
Need to keep a list of your pages
Buckets (I have a lot of time to lose)
This is the only method of the three I have not attempted to implement myself, so I can only speak of the Theory:
struct bucket
{
size_t buck_num; //number of data segment
size_t buck_size; //size of a data segment
void *page;
void *freeinfo;
}
- You have from the start a few small pages, each split in blocks of constant size (one 8-bytes page, one 16-bytes, one 32-bytes and so on)
- The "freedom information" of those data buckets are stored in bitsets (structures representing a large set of ints) either at the start of each page, or in a separate memory zone.
for example, for a 512-bytes bucket in a 4096 bytes pages, the bitset representing it would be a 8-bit bitset,
supposing *freeinfo = 01001000
, this would mean the second and fifth buckets are free.
Pros: By far the fastest and cleanest over the long run,
Most efficient on many small allocations
Cons: Very cumbersome to implement, quite heavy for a small program, need for a separate memory space for bitsets.
There are probably other algorithms and implementations but those three are the most used, So I hope you can get a lead on what you want to do from this.