Last active
February 28, 2024 20:27
-
-
Save bellbind/507a426b0958eedb0fcf to your computer and use it in GitHub Desktop.
[c] malloc and free implementation on the static memory pool
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# How to build and to run | |
# mkdir build ; cd build/ ; cmake .. ; make ; ./test | |
# (cleanup: rm -r build/) | |
set(CMAKE_C_STANDARD 11) | |
list(APPEND CMAKE_C_FLAGS "-Wall -Wextra -pedantic") | |
add_library(mymalloc SHARED mymalloc.c) | |
add_executable(test test.c) | |
link_directories(.) | |
target_link_libraries(test mymalloc) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// malloc and free implementaion on the static memory pool | |
// see https://en.wikipedia.org/wiki/C_dynamic_memory_allocation | |
#include "mymalloc.h" | |
union chunk; | |
struct info { | |
union chunk* prev; | |
union chunk* next; | |
size_t size; | |
}; | |
struct node { | |
struct info info; | |
size_t used; | |
}; | |
union chunk { | |
unsigned char data[sizeof (struct node)]; | |
struct node node; | |
}; | |
// static memory | |
enum {CHUNK_MAX = 1024}; | |
static size_t initialized = 0; | |
static const size_t chunk_max = CHUNK_MAX; | |
static union chunk pool[CHUNK_MAX]; | |
static union chunk head = {.node = {{NULL, NULL, 0}, 0}}; | |
static union chunk tail = {.node = {{NULL, NULL, 0}, 0}}; | |
static void init() { | |
head.node.info.next = pool; | |
union chunk* prev = &head; | |
union chunk* curr = pool; | |
for (size_t i = 0; i < chunk_max; i++) { | |
curr->node.info.prev = prev; | |
curr->node.info.next = curr + 1; | |
curr->node.info.size = 0; | |
curr->node.used = 0; | |
prev = curr++; | |
} | |
prev->node.info.next = &tail; | |
tail.node.info.prev = prev; | |
initialized = 1; | |
} | |
static size_t chunk_count(size_t size) { | |
size_t chunks = 0; | |
size_t chunks_size = 0; | |
while (chunks_size < size + sizeof (struct info)) { | |
chunks_size += sizeof (union chunk); | |
chunks++; | |
} | |
return chunks; | |
} | |
static void* unlink(union chunk* top, size_t count) { | |
union chunk* prev = top->node.info.prev; | |
union chunk* next = top[count - 1].node.info.next; | |
prev->node.info.next = next; | |
next->node.info.prev = prev; | |
top->node.info.size = count; | |
return top->data + sizeof (struct info); | |
} | |
static void relink(union chunk* top) { | |
if (top < pool || pool + chunk_max - 1 < top) return; | |
union chunk* prev = &head; | |
while (prev->node.info.next != &tail && prev->node.info.next < top) { | |
prev = prev->node.info.next; | |
} | |
if (prev == top) return; //dual free | |
union chunk* next = prev->node.info.next; | |
union chunk* curr = top; | |
prev->node.info.next = curr; | |
size_t count = top->node.info.size; | |
for (size_t i = 0; i < count; i++) { | |
curr->node.info.prev = prev; | |
curr->node.info.next = curr + 1; | |
curr->node.info.size = 0; | |
curr->node.used = 0; | |
prev = curr++; | |
} | |
prev->node.info.next = next; | |
next->node.info.prev = prev; | |
} | |
extern void* mymalloc(size_t size) { | |
if (!initialized) init(); | |
size_t chunks = chunk_count(size); | |
size_t keep = 0; | |
union chunk* top = head.node.info.next; | |
for (union chunk* curr = top; curr != &tail; curr = curr->node.info.next) { | |
keep++; | |
if (keep == chunks) return unlink(top, chunks); | |
if (curr->node.info.next == curr + 1) continue; | |
keep = 0; | |
top = curr->node.info.next; | |
} | |
return NULL; | |
} | |
extern void myfree(void* ptr) { | |
if (ptr == NULL) return; | |
char* bptr = ptr; | |
ptr = bptr - sizeof (struct info); | |
relink(ptr); | |
} | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef __MYMALLOC_H_ | |
#define __MYMALLOC_H_ | |
#include <stddef.h> | |
extern void* mymalloc(size_t size); | |
extern void myfree(void* ptr); | |
#endif |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// mymalloc example | |
// clang -Wall -Wextra -std=c11 mymalloc.c test.c -o mymalloc_test | |
#include <stdio.h> | |
#include "mymalloc.h" | |
int main() { | |
size_t* buf1 = mymalloc(sizeof (size_t) * 4); | |
for (int i = 0; i < 4; i++) buf1[i] = i + 1; | |
size_t* buf2 = mymalloc(sizeof (size_t) * 4); | |
for (int i = 0; i < 4; i++) buf2[i] = (i + 1) * 16; | |
printf("buf1(%p): %zx, %zx, %zx, %zx\n", | |
buf1, buf1[0], buf1[1], buf1[2], buf1[3]); | |
printf("buf2(%p): %zx, %zx, %zx, %zx\n", | |
buf2, buf2[0], buf2[1], buf2[2], buf2[3]); | |
myfree(buf1); | |
// area resued by same size of freed buf1 | |
size_t* buf3 = mymalloc(sizeof (size_t) * 4); | |
printf("buf3(%p) == buf1\n", buf3); // same as buf1 | |
myfree(buf3); | |
// buf1 area not reused because not enough size from buf1 | |
size_t* buf4 = mymalloc(sizeof (size_t) * 8); | |
printf("buf4(%p) > buf2\n", buf4); // diffrent from buf1 | |
myfree(buf4); | |
myfree(buf2); | |
// 1 chunk x 3 | |
size_t* buf5 = mymalloc(sizeof (size_t)); | |
printf("buf5(%p) == buf1\n", buf5); | |
size_t* buf6 = mymalloc(sizeof (size_t)); | |
printf("buf6(%p) == buf1 + 0x20\n", buf6); | |
size_t* buf7 = mymalloc(sizeof (size_t)); | |
printf("buf7(%p) == buf1 + 0x40\n", buf7); | |
myfree(buf5); | |
myfree(buf6); | |
myfree(buf7); | |
return 0; | |
} |
Hi,
Can I ask what is the motivation of declaring chunk as a union?
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Design of the
mymalloc
:union chunk
is anode
of a special double lineked list(node.info.prev
andnode.info.next
) or mid of allocated memory.node.used
is a start of the allocated memory (somymalloc
returns the addres ofnode.used
), not allocation flag.node.info.size
is a count ofchunk
s as a continuing allocatedchunk
area (the count includes itself).pool
is a array ofchunk
s.NOTE: a free
node
is just a single independent node, not a bunch ofchunk
s (info.size
is always 0).union chunk
is 4x pointer size(32bytes in 64bit target) block unit.init
makes a double list to the wholepool
as everychunk
as unusednode
mymalloc
searches a continuingchunk
area to enough to allocate from thepool
.unlink
removes thechunk
s to thepool
relink
(called inmyfree
) adds as the releasedchunk
s as free double linked listnode
s.avoid to use specific sized
int
s and their literals , instead of usesize_t
.