This commit is contained in:
yuchen 2015-03-22 21:41:31 +08:00
parent 324f12ecfa
commit 8cf5bb31ea
6 changed files with 401 additions and 0 deletions

View File

@ -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.

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,36 @@
#include "memory.h"
#include <stdio.h>
#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();
}

View File

@ -0,0 +1,303 @@
#include "memory.h"
#include <stdlib.h> /* for malloc */
#include <stdio.h> /* for printf (for debugging) */
#include <string.h> /* for memcpy */
#include <inttypes.h>
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;
}

View File

@ -0,0 +1,13 @@
#ifndef MEMORY_H_
#define MEMORY_H_
#include <stddef.h> /* for size_t */
void * dcmalloc(size_t size);
void dcfree(void * ptr);
void * dcmemset(void * ptr, int value, size_t num);
void memoryDump();
#endif