Skip to content

Instantly share code, notes, and snippets.

@DmitrySoshnikov
Created May 17, 2017 18:59
Show Gist options
  • Save DmitrySoshnikov/f61e72f0e88b5b9b6ed13c296bde38e3 to your computer and use it in GitHub Desktop.
Save DmitrySoshnikov/f61e72f0e88b5b9b6ed13c296bde38e3 to your computer and use it in GitHub Desktop.
Educational memory allocator
/**
* Simple and educational `malloc` implementation.
*
* Dmitry Soshnikov <[email protected]>
*
* Maintains explicit linked list of allocated memory blocks. Each block
* has a header, containing meta-information, such as whether a block is
* free, its size, and a reference to the next block.
*
* Homework assignments:
*
* - Implement "splitting" procedure (if an allocation request is smaller
* than the first free block, split the free block)
*
* - Implement coalescing of blocks on free operation (merge two or more
* adjacent free blocks).
*
* - Implement "explicit free list" (the "next" pointer points not to just
* the next block, but to an explicit "free" block). Faster allocation.
*
* - Implement "segregated free list" (partition the allocations by block
* size, power of 2). Faster allocation.
*/
#include <iostream>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
using namespace std;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
/**
* Memory blocks alignment by 4.
*
* malloc(4) -> 4
* malloc(2) -> 4
* malloc(5) -> 8
* etc.
*/
#define ALLIGN_4(x) (((((x) - 1) >> 2) << 2) + 4)
/**
* Memory block.
*
* Consists of a header, and a pointer to the payload
* returned to a user.
*/
struct Block {
/**
* Meta-data header.
*/
int free;
size_t size;
Block* next;
/**
* Payload pointer.
*/
char data[1];
};
#define BLOCK_SIZE sizeof(struct Block)
/**
* Initial base block.
*/
Block* base = NULL;
/**
* Finds a block of a requested size.
*/
Block* find_block(Block** last, size_t size) {
Block* block = base;
while (block && !(block->free && block->size >= size)) {
*last = block;
block = block->next;
}
return block;
}
/**
* Returns a pointer to an object header.
*/
Block* get_block(char* p) {
return (Block*)(p - BLOCK_SIZE + 8);
}
/**
* Extends the heap, by moving the heap-break pointer.
*/
Block* extend_heap(Block* last, size_t size) {
// Current heap break.
Block* block = (Block*)sbrk(0);
// See if we can map more memory.
if (sbrk(BLOCK_SIZE + size) == (void*) - 1) {
return NULL;
}
block->size = size;
block->next = NULL;
if (last) {
last->next = block;
}
block->free = 0;
return block;
}
/**
* Allocates the memory from heap of a needed size.
*
* Returns a void-pointer to the payload (the pointer should be cast).
*/
void* malloc(size_t size) {
Block* block;
Block* last;
// Align the size.
size = ALLIGN_4(size);
if (base) {
last = base;
block = find_block(&last, size);
// If we found a block of needed size.
if (block) {
block->free = 0;
} else {
// Otherwise, map more memory.
block = extend_heap(last, size);
// OOM.
if (!block) {
return NULL;
}
}
} else {
// First time allocation, move the heap-break.
block = extend_heap(NULL, size);
// OOM.
if (!block) {
return NULL;
}
base = block;
}
// Payload.
return block->data;
}
void print_block_info(char* p) {
Block* block = get_block(p);
cout << endl << "Block info for \"" << p << "\":" << endl;
cout << " addr: " << (void*)block << endl;
cout << " free: " << boolalpha << (block->free ? true : false) << endl;
cout << " size: " << block->size << endl;
cout << " next: " << block->next << endl;
cout << " data: " << block->data << endl << endl;
}
/**
* Simple and naive implementation of `free`. See homework assignments
* to do more bookkeeping on free (coalesce the free blocks, etc).
*/
void free(char* p) {
Block* block = get_block(p);
cout << endl << "-- Freeing: " << (void*)block << endl << endl;
block->free = 1;
}
int main(int argc, char const *argv[]) {
char* c1 = (char*)malloc(4);
char* c2 = (char*)malloc(8);
strcpy(c2, "data");
cout << "Data c2: " << c2 << endl;
strcpy(c1, "1234");
cout << "Data c1: " << c1 << endl;
print_block_info(c1);
print_block_info(c2);
// Free c2, c3 will be able to reuse.
free(c2);
char* c3 = (char*)malloc(8);
strcpy(c3, "abcd");
cout << "Data c3: " << c3 << endl;
print_block_info(c2); // same as 3, became "abcd"
print_block_info(c3);
return 0;
}
// Results:
/*
Data c2: data
Data c1: 1234
Block info for "1234":
addr: 0x10e571000
free: false
size: 4
next: 0x10e571024
data: 1234
Block info for "data":
addr: 0x10e571024
free: false
size: 8
next: 0x0
data: data
-- Freeing: 0x10e571024
Data c3: abcd
Block info for "abcd":
addr: 0x10e571024
free: false
size: 8
next: 0x0
data: abcd
Block info for "abcd":
addr: 0x10e571024
free: false
size: 8
next: 0x0
data: abcd
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment