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; | |
| } |
Author
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 chunkis anodeof a special double lineked list(node.info.prevandnode.info.next) or mid of allocated memory.node.usedis a start of the allocated memory (somymallocreturns the addres ofnode.used), not allocation flag.node.info.sizeis a count ofchunks as a continuing allocatedchunkarea (the count includes itself).poolis a array ofchunks.NOTE: a free
nodeis just a single independent node, not a bunch ofchunks (info.sizeis always 0).union chunkis 4x pointer size(32bytes in 64bit target) block unit.initmakes a double list to the wholepoolas everychunkas unusednodemymallocsearches a continuingchunkarea to enough to allocate from thepool.unlinkremoves thechunks to thepoolrelink(called inmyfree) adds as the releasedchunks as free double linked listnodes.avoid to use specific sized
ints and their literals , instead of usesize_t.