From 8cf5bb31eaf736c03b3d20733e76c425dc62ffe9 Mon Sep 17 00:00:00 2001 From: yuchen Date: Sun, 22 Mar 2015 21:41:31 +0800 Subject: [PATCH] add K&R malloc codes from https://github.com/wbraynen/malloc --- related_info/lab2/kr_malloc_free_2/LICENSE | 21 ++ related_info/lab2/kr_malloc_free_2/Makefile | 14 + related_info/lab2/kr_malloc_free_2/README.md | 14 + related_info/lab2/kr_malloc_free_2/main.c | 36 +++ related_info/lab2/kr_malloc_free_2/memory.c | 303 +++++++++++++++++++ related_info/lab2/kr_malloc_free_2/memory.h | 13 + 6 files changed, 401 insertions(+) create mode 100644 related_info/lab2/kr_malloc_free_2/LICENSE create mode 100644 related_info/lab2/kr_malloc_free_2/Makefile create mode 100644 related_info/lab2/kr_malloc_free_2/README.md create mode 100644 related_info/lab2/kr_malloc_free_2/main.c create mode 100644 related_info/lab2/kr_malloc_free_2/memory.c create mode 100644 related_info/lab2/kr_malloc_free_2/memory.h diff --git a/related_info/lab2/kr_malloc_free_2/LICENSE b/related_info/lab2/kr_malloc_free_2/LICENSE new file mode 100644 index 0000000..80b034a --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 villi19 + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/related_info/lab2/kr_malloc_free_2/Makefile b/related_info/lab2/kr_malloc_free_2/Makefile new file mode 100644 index 0000000..2b1d754 --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/Makefile @@ -0,0 +1,14 @@ +binaries=memory.o main.o + +all: + gcc -g -ansi -c *.c main.c #-m32: this makes a 32-bit build, but you might have to "sudo apt-get install libc6-dev-i386" first + echo Compiling done + + gcc -g -ansi *.o -o main + echo Linking done + + #make clean #uncomment this if you don't care to debug, symbols and all + +clean: + rm -f $(binaries) + echo Clean done \ No newline at end of file diff --git a/related_info/lab2/kr_malloc_free_2/README.md b/related_info/lab2/kr_malloc_free_2/README.md new file mode 100644 index 0000000..08179cf --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/README.md @@ -0,0 +1,14 @@ +malloc +====== +WHAT THIS IS: This revisit of the K&R classic of systems programming was inspired by recent problems in concurrent programming. + +TO ELABORATE: The malloc code is, more or less, but an implementation of what I saw described in the K&R C bible and is written in Ansi C. Conceptually this is textbook K&R stuff though much nicer than the snippet of code K&R include. So it's merely a starting point or a baseline over which one might want to optimize the allocator. However, it is the context that is interesting. + +CONTEXT: This was in the context of looking at Google's TCMalloc and thinking about when it might be worth it to implement a custom memory allocator and what optimization techniques in this space might look like. But even if you use TCMalloc for concurrent programming, TCMalloc uses mmap and so, if you want to use your own memory mapper, e.g. something like this -- http://eurosys2013.tudos.org/wp-content/uploads/2013/paper/Merryfield.pdf -- you'd have to either modify TCMalloc or build your own allocator. + +NOTES: I get memory from the system using the standard malloc call in the C library and then manage that memory myself. But if for some reason you wanted to bypass malloc entirely, see the Malloc Tutorial below. + +REFERENCES: +K&R: http://en.wikipedia.org/wiki/File:The_C_Programming_Language_cover.svg, +Malloc Tutorial: http://www.inf.udec.cl/~leo/Malloc_tutorial.pdf, +For further pondering, see http://www.stanford.edu/~hlitz/papers/asplos-litz.pdf diff --git a/related_info/lab2/kr_malloc_free_2/main.c b/related_info/lab2/kr_malloc_free_2/main.c new file mode 100644 index 0000000..0b6b229 --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/main.c @@ -0,0 +1,36 @@ +#include "memory.h" +#include + +#define SAFE_FREE(ptr) if (ptr) dcfree(ptr) +#define SAFE_MEMSET(ptr, value, size) if (ptr) dcmemset(ptr, value, size) + +int main() +{ + const int + na = 6, + nb = 2, + nc = 912, + nd = 4; + + char *a = (char *)dcmalloc(na * sizeof( char)); + SAFE_MEMSET(a, 'a', na); + + char *b = (char *)dcmalloc(nb * sizeof(char)); + SAFE_MEMSET(b, 'b', nb); + + char *c = (char *)dcmalloc(nc * sizeof(char)); + SAFE_MEMSET(c, 'c', nc); + + SAFE_FREE(c); + + char *d = (char *)dcmalloc(nd * sizeof(char)); + SAFE_MEMSET(d, 'd', nd); + + memoryDump(); + + SAFE_FREE(b); + SAFE_FREE(a); + SAFE_FREE(d); + + memoryDump(); +} diff --git a/related_info/lab2/kr_malloc_free_2/memory.c b/related_info/lab2/kr_malloc_free_2/memory.c new file mode 100644 index 0000000..288720a --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/memory.c @@ -0,0 +1,303 @@ +#include "memory.h" +#include /* for malloc */ +#include /* for printf (for debugging) */ +#include /* for memcpy */ +#include + +const size_t g_totalMemorySize = 1024; /* bytes */ +unsigned char *g_heapsBase = NULL; +unsigned char *g_heapsEnd = NULL; + +#define TRUE 1 +#define FALSE 0 +#define BOOLEAN int + +#define ALIGN(size) ((size + 7) & ~0x7) + +typedef struct block * BlockPtr; +struct block +{ + BOOLEAN isFree; + size_t size; + BlockPtr next; + BlockPtr previous; + char data[1]; /* marks the start of the actual data segment */ +}; +const HEADER_SIZE = offsetof(struct block, data); + +static int _assert(BOOLEAN condition, int line); /* for line, pass in __LINE__ */ +static BlockPtr _findBlock( /*BlockPtr last, */ size_t size); +static void _splitBlock(BlockPtr b, size_t size); +static void _initHeap(); +static BOOLEAN _isValidAddress(void *ptr); + +void memoryDump() +{ + BlockPtr b = NULL; + unsigned char * p = NULL; + int col = 0; + unsigned char * snapshot = (unsigned char *)malloc(g_totalMemorySize); + + printf("\n----------- heap metadata --------------\n"); + printf("Our heap's total size: %zd\n", g_totalMemorySize); + printf("HEADER_SIZE: %d\n", HEADER_SIZE); + printf("So max total net available memory is: %zd\n\n", g_totalMemorySize - HEADER_SIZE); + memcpy(snapshot, g_heapsBase, g_totalMemorySize); + + /* + * Make headers human-readable + */ + int blockCounter = 0; + for (b = (BlockPtr) g_heapsBase; b != NULL; b = b->next) + { + /* + * Pad the number with spaces so the whole message is the size of the header: + * 32 bytes on a 64-bit machine (should be 28 bytes but 32 because of alignment? or am I miscounting?) + * 16 bytes on a 32-bit machine. + */ + char msg[100]; + sprintf(msg, "[%16zd bytes: ]", b->size); + + size_t offset = (unsigned char*)b - (unsigned char*)g_heapsBase; + _assert(offset >= 0 && offset < g_totalMemorySize, __LINE__); + + memcpy((unsigned char*)((size_t)snapshot + offset), msg, HEADER_SIZE); + + size_t withHeader = b->size + HEADER_SIZE; + printf("Block %d, %3zd B (%3zd B) at %p (after header), is %s\n", /* replace the size_t prints %d by %zd on Ubuntu */ + ++blockCounter, + b->size, + withHeader, + b->data, + b->isFree ? "FREE" : "OCCUPIED"); + } + printf("\n"); + + /* + * Print the heap snapshot + */ + printf("------------ heap content: beginning memory dump ---------------\n"); + for (p = snapshot; p < (snapshot + g_totalMemorySize); p++) + { + if (*p && *p != '\n') + { + /* print the symbol at that location as a character */ + putchar(*p); + } + else + { + putchar(178); + } + + /* insert periodic line breaks */ + col++; + if (col % 64 == 0) + { + putchar('\n'); + } + } + + printf("------------- ending memory dump :heap content -----------------\n\n"); +} + +static void _initHeap() +{ + g_heapsBase = malloc(g_totalMemorySize); + g_heapsEnd = g_heapsBase + g_totalMemorySize; + + /* initially, the heap is one giant block (which gets divvied up and fragmented over time as people make dcmalloc requests) */ + BlockPtr newBlock; + newBlock = (BlockPtr)(g_heapsBase); + newBlock->size = g_totalMemorySize - HEADER_SIZE; + newBlock->isFree = TRUE; + newBlock->next = NULL; + newBlock->previous = NULL; +} + +/** +* assuming we still have memory left +* TO DO: add an _assert() +*/ +void * dcmalloc(size_t size) +{ + BlockPtr b = NULL; + + size = ALIGN(size); + + if (!g_heapsBase) + { + _initHeap(); + } + + b = _findBlock(size); + if (!b) + { + printf("Sorry, not enough memory left to allocate %zd bytes.\n", size); + return NULL; + } + b->isFree = FALSE; + _splitBlock(b, size); + + printf("allocating at %p, %zd bytes\n", b, size); + return b->data; +} + +void _merge(BlockPtr b, BlockPtr nextBlock) +{ + /* assume b1 and b2 are neighbors, both are free, and b1 is right before b2 */ + _assert(b->isFree && nextBlock->isFree, __LINE__); /* both blocks think they are free */ + _assert(b->next == nextBlock, __LINE__); /* b1 thinks that b2 is after her */ + _assert(b == nextBlock->previous, __LINE__); /* b2 thinks that b1 is before him */ + _assert(b < nextBlock, __LINE__); /* b1 is before b2 */ + + b->size += (HEADER_SIZE + nextBlock->size); + b->next = nextBlock->next; /* skip next block */ + if (nextBlock->next) + { + nextBlock->next->previous = b; + } + + memset(b->data, '-', b->size); +} + +/* + * Important: if ptr is NULL, this will return TRUE. + * + * But if ptr is not NULL (i.e. has no excuse) and is + * not within the bounds of our heap, then this will return FALSE + */ +BOOLEAN _isValidAddress(void *ptr) +{ + unsigned char *p = (unsigned char *)ptr; + + BOOLEAN isValid = TRUE; + + if (p) + { + isValid = + p <= g_heapsEnd && + p >= g_heapsBase; + } + return isValid; +} + +void dcfree(void *ptr) +{ + BlockPtr b = (BlockPtr) (ptr - HEADER_SIZE); + + printf("freeing %p\n", ptr); + _assert(b && _isValidAddress(b), __LINE__); + _assert(_isValidAddress(b->next), __LINE__); + _assert(_isValidAddress(b->previous), __LINE__); + + b->isFree = TRUE; + dcmemset(b->data, '-', b->size); + + /* fuse with the next block if it's also free */ + if (b->next) + { + if (b->next->isFree) + { + _merge(b, b->next); + } + } + + if (b->previous && b->previous->isFree) + { + _merge(b->previous, b); + } +} + + +void * dcmemset(void * ptr, int value, size_t num) +{ + int c = value; + size_t i = 0; + unsigned char *p = (unsigned char *)ptr; + + for (i = 0; i < num; i++) + { + *p = (unsigned char)c; + p++; + } + + return ptr; +} + +static BlockPtr _findBlock( size_t size ) +{ + BlockPtr b = (BlockPtr) g_heapsBase; + _assert(b != NULL, __LINE__); + while (b && !(b->isFree && size <= b->size)) + { + b = b->next; + } + return b; +} + + +/* + * Arguments: + * size - size without the header; i.e. how much data, at a minimum, do you need this block to hold? + * b - block to split + * + * The simple picture: + * Before: |------------------ block b ------------------| + * After: |--- block b ---|--------- new block ---------| + * + * Or, to be more precise, with headers (marked with an 'H'): + * Before: |H----------------- block b ------------------| + * After: |H-- block b ---|H-------- new block ---------| + */ +static void _splitBlock(BlockPtr b, size_t size) +{ + if (size >= b->size) + { + return; + } + + /* create a second block */ + BlockPtr newBlock; + unsigned char *p = b->data + size; + newBlock = (BlockPtr)p; + newBlock->size = b->size - size - HEADER_SIZE; + newBlock->next = b->next; + newBlock->previous = b; + newBlock->isFree = TRUE; + + /* update the original block */ + b->size = size; + b->next = newBlock; + + /* debug */ + _assert((unsigned char*) newBlock->next < g_heapsEnd, __LINE__); + if ((unsigned char *) newBlock->next >= g_heapsEnd) + { + printf("splitBlock: invalid address!"); + getchar(); + return; + } + + _assert((unsigned char *) b->next < g_heapsEnd, __LINE__); + if ((unsigned char *) b->next >= g_heapsEnd) + { + printf("splitBlock: invalid address 2!"); + getchar(); + return; + } +} + + + +static int _assert(BOOLEAN condition, int line) +{ + if (!condition) + { + fprintf(stderr, + "_assertION FAILURE: at %s, line %d\n", + __FILE__, + line); + return FALSE; + } + return TRUE; +} diff --git a/related_info/lab2/kr_malloc_free_2/memory.h b/related_info/lab2/kr_malloc_free_2/memory.h new file mode 100644 index 0000000..75c815e --- /dev/null +++ b/related_info/lab2/kr_malloc_free_2/memory.h @@ -0,0 +1,13 @@ +#ifndef MEMORY_H_ +#define MEMORY_H_ + +#include /* for size_t */ + +void * dcmalloc(size_t size); +void dcfree(void * ptr); + +void * dcmemset(void * ptr, int value, size_t num); + +void memoryDump(); + +#endif