《操作系统》的实验代码。
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

303 lines
7.4 KiB

#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;
}