初始化内存堆部分:
cfe_main()
{
/*
* Initialize the memory allocator
*/
KMEMINIT((unsigned char *) (uintptr_t) mem_heapstart,
((CFG_HEAP_SIZE)*1024));
}
#define KMEMINIT(buffer,length) kmeminit(&kmempool,(buffer),(length))
mempool_t kmempool; /* default pool */
//编译期分配的内存空间
struct mempool_s {
memnode_t *root; /* pointer to root node */
unsigned char *base; /* base of memory region */
unsigned int length; /* size of memory region */
};
typedef enum { memnode_free = 0, memnode_alloc } memnode_status_t;
typedef struct memnode_s {
unsigned int seal;
struct memnode_s *next; /* pointer to next node */
unsigned int length; /* length of the entire data section */
memnode_status_t status; /* alloc/free status */
unsigned char *data; /* points to actual user data */
void *memnodeptr; /* memnode back pointer (see comments) */
} memnode_t;
void kmeminit(mempool_t *pool,unsigned char *buffer,int length)
{
pool->root = (memnode_t *) buffer;
//在空块内存的头部创建节点
pool->root->seal = MEMNODE_SEAL;
//魔数
#define MEMNODE_SEAL 0xFAAFA123 /* just some random constant */
pool->root->length = length - sizeof(memnode_t);
//减掉头部剩下的内存大小
pool->root->data = memnode_data(unsigned char *,pool->root);
//指针偏移+头部的大小
pool->root->status = memnode_free;
//标记内存块为free状态
pool->root->next = NULL;
//指向下一个内存块
pool->base = buffer;
//内存池的基地址
pool->length = length;
//内存池的长度
}
#define memnode_data(t,m) (t) (((memnode_t *) (m))+1)
分配内存使用
KMALLOC:
#define KMALLOC(size,align) kmalloc(&kmempool,(size),(align))
/* *********************************************************************
* kmalloc(pool,size,align)
*
* Allocate some memory from the pool.
*
* Input parameters:
* pool - pool structure
* size - size of item to allocate
* align - alignment (must be zero or a power of 2)
*
* Return value:
* pointer to data, or NULL if no memory left
********************************************************************* */
void *kmalloc(mempool_t *pool,unsigned int size,unsigned int align)
{
memnode_t *m;
memnode_t *newm;
memnode_t **backptr;
uintptr_t daddr = 0;
uintptr_t realsize = 0;
uintptr_t extra;
uintptr_t blkend;
uintptr_t ptralign;
/*
* Everything should be aligned by at least the
* size of an int64
*/
ptralign = (uintptr_t) align;
if (ptralign < sizeof(void *)) ptralign = sizeof(uint64_t);
//最小对齐单元为
int64
/*
* Everything should be at least a multiple of the
* size of a pointer.
*/
if (size == 0) size = sizeof(void *);
//分配内存大size必须要是4字节的整数倍
if (size & (sizeof(void *)-1)) {
size += sizeof(void *);
size &= ~(sizeof(void *)-1);
}
/*
* Find a memnode at least big enough to hold the storage we
* want.
*/
for (m = pool->root; m; m = m->next) {
if (m->status == memnode_alloc) continue;
/*
* If we wanted a particular alignment, we will
* need to adjust the size.
*/
daddr = memnode_data(uintptr_t,m);
extra = 0;
if (daddr & (ptralign-1)) {
extra = ptralign - (daddr & (ptralign-1));
}
realsize = size + extra;
//加上对齐需要浪费的内存
if (m->length < realsize) continue;
break;
}
/*
* If m is null, there's no memory left.
*/
if (m == NULL) {
lib_outofmemory();
return NULL;
}
/*
* Otherwise, use this block. Calculate the address of the data
* to preserve the alignment.
*/
if (daddr & (ptralign-1)) {
daddr += ptralign;
daddr &= ~(ptralign-1);
}
/* Mark this node as allocated. */
m->data = (unsigned char *) daddr;
//存放对齐后分配的数据的起始地址
m->status = memnode_alloc;
/*
* Okay, this is ugly. Store a pointer to the original
* memnode just before what we've allocated. It's guaranteed
* to be aligned at least well enough for this pointer.
*
If for some reason the memnode was already exactly
* aligned, backing up will put us inside the memnode
* structure itself... that's why the memnodeptr field
* is there, as a placeholder for this eventuality
.
*/
backptr = (memnode_t **) (m->data - sizeof(memnode_t *));
//将头结点的位置存放在分配的内存的头四个字节中
*backptr = m;
/*
* See if we need to split it.
* Don't bother to split if the resulting size will be
* less than MINBLKSIZE bytes
*/
if (m->length - realsize < MINBLKSIZE) {
//
#define MINBLKSIZE 64 如果剩余的内存小于64个字节,则直接不分割当前内存块,因为头结点还要占据一些字节
return m->data;
}
/*
* Split this block. Align the address on a pointer-size
* boundary.
*/
daddr += size;
if (daddr & (uintptr_t)(sizeof(void *)-1)) {
//4字节对齐
daddr += (uintptr_t)sizeof(void *);
daddr &= ~(uintptr_t)(sizeof(void *)-1);
}
blkend = memnode_data(uintptr_t,m) + (uintptr_t)(m->length);
//当前内存块的结束地址
newm = (memnode_t *) daddr;
newm->next = m->next;
m->length = (unsigned int) (daddr - memnode_data(uintptr_t,m));
//除去头部后内存块的长度,包括未对齐的内存
m->next = newm;
m->status = memnode_alloc;
newm->seal = MEMNODE_SEAL;
newm->data = memnode_data(unsigned char *,newm);
newm->length = (unsigned int) (blkend - memnode_data(uintptr_t,newm));
newm->status = memnode_free;
//标记剩余的内存卡为free状态
return m->data;
}
通过上面的分析可以看出,其实data这个字段可以去掉,因为它的左右不大,对应free的内存块,它指向的是头结点后紧跟在的内存地址,对于分配了的内存块来说,它存放的是头结点后对齐后的内存地址,也即分配返回的地址,而这个可以通过前面紧跟着的4个字节获取头结点的位置,所以说data字段是可以去掉的。
length字段始终存放的是内存块去掉头结点后的大小,包括所有未对齐的内存
内存块链表是按照内存地址顺序连接的,这样做的目的是为了方便内存合并,见下文解释。
释放内存使用
KFREE:
#define KFREE(ptr) kfree(&kmempool,(ptr))
/* *********************************************************************
* kfree(ptr)
*
* Return some memory to the pool.
*
* Input parameters:
* ptr - pointer to something allocated via kmalloc()
*
* Return value:
* nothing
********************************************************************* */
void kfree(mempool_t *pool,void *ptr)
{
memnode_t **backptr;
memnode_t *m;
if (((unsigned char *) ptr < pool->base) ||
//检查要释放的地址是否在内存池地址范围内
((unsigned char *) ptr >= (pool->base+pool->length))) {
#ifdef TESTPROG
printf("Pointer %08X does not belong to pool %08X\n",ptr,pool);
#endif
return;
}
backptr = (memnode_t **) (((unsigned char *) ptr) - sizeof(memnode_t *));
m = *backptr;
//获取头结点的位置
if (m->seal != MEMNODE_SEAL) {
//判断头的魔数
#ifdef TESTPROG
printf("Invalid node freed: %08X\n",m);
#endif
return;
}
m->status = memnode_free;
//标记为free状态
kmemcompact(pool);
//进行内存紧缩,即合并临近的空闲内存块,防止内存碎片
}
/* *********************************************************************
* kmemcompact(pool)
*
* Compact the memory blocks, coalescing consectutive free blocks
* on the list.
*
* Input parameters:
* pool - pool descriptor
*
* Return value:
* nothing
********************************************************************* */
static void kmemcompact(mempool_t *pool)
{
memnode_t *m;
int compacted;
do {
compacted = 0;
for (m = pool->root; m; m = m->next) {
//遍历内存池中的所有内存块
/* Check seal to be sure that we're doing ok */
if (m->seal != MEMNODE_SEAL) {
#ifdef TESTPROG
printf("Memory list corrupted!\n");
#endif
return;
}
/*
* If we're not on the last block and both this
* block and the next one are free, combine them
*/
if (m->next &&
(m->status == memnode_free) &&
(m->next->status == memnode_free)) {
//连续两块内存为free状态
m->length += sizeof(memnode_t) + m->next->length;
m->next->seal = 0;
m->next = m->next->next;
compacted++;
}
/* Keep going till we make a pass without doing anything. */
}
} while (compacted > 0);
//当有内存卡合并发生时,重新遍历一遍
}
通过以上的代码可以看出该malloc实现算法非常简单,缺点是直接找到第一块大内存进行分割,这样很容易产生碎片。
对应的内存分配测试程序如下所示:
#ifdef TESTPROG
int kmemchk(mempool_t *pool,int verbose)
{
memnode_t *m;
memnode_t **backptr;
unsigned int daddr;
for (m = pool->root; m; m = m->next) {
if (verbose) {
printf("%08X: Next=%08X Len=%5u %s Data=%08X ",
m,m->next,m->length,
m->status ? "alloc" : "free ",
m->data);
}
daddr = memnode_data(uintptr_t,m);
if (m->seal != MEMNODE_SEAL) {
if (verbose) printf("BadSeal ");
else return -1;
}
if (m->next && (daddr + m->length != (unsigned int) m->next)) {
if (verbose) printf("BadLength ");
else return -1;
}
if (m->next && (m->next < m)) {
if (verbose) printf("BadOrder ");
else return -1;
}
if (m->data < (unsigned char *) m) {
if (verbose) printf("BadData ");
else return -1;
}
if (m->status == memnode_alloc) {
backptr = (memnode_t **) (m->data - sizeof(void *));
if (*backptr != m) {
if (verbose) printf("BadBackPtr ");
else return -1;
}
}
if (verbose) printf("\n");
}
return 0;
}
#define MEMSIZE 1024*1024
unsigned char *ptrs[4096];
unsigned int sizes[4096];
/* *********************************************************************
* main(argc,argv)
*
* Test program for the memory allocator
*
* Input parameters:
* argc,argv
*
* Return value:
* nothing
********************************************************************* */
void main(int argc,char *argv[])
{
unsigned char *mem;
int items = 0;
int idx;
int size;
int totalsize = 0;
int nfree,freecnt;
mempool_t *pool = &kmempool;
mem = malloc(MEMSIZE);
kmeminit(pool,mem,MEMSIZE);
items = 0;
for (;;) {
for (;;) {
if (items == 4096) break;
size = rand() % 1024;
ptrs[items] = kmalloc(pool,size,1<<(rand() & 7));
if (!ptrs[items]) break;
sizes[items] = size;
items++;
totalsize += size;
}
printf("%d items allocated, %d total bytes\n",items,totalsize);
if (kmemchk(pool,0) < 0) {
kmemchk(pool,1);
exit(1);
}
/* Scramble the pointers */
idx = items - 1;
while (idx) {
if (rand() & 2) {
mem = ptrs[0];
ptrs[0] = ptrs[idx];
ptrs[idx] = mem;
nfree = sizes[0];
sizes[0] = sizes[idx];
sizes[idx] = nfree;
}
idx--;
}
/* now free a random number of elements */
nfree = rand() % items;
freecnt = 0;
for (idx = nfree; idx < items; idx++) {
kfree(pool,ptrs[idx]);
totalsize -= sizes[idx];
freecnt++;
ptrs[idx] = NULL;
sizes[idx] = 0;
if (kmemchk(pool,0) < 0) {
kmemchk(pool,1);
exit(1);
}
}
items -= freecnt;
printf(".");
}
kmemchk(pool,1);
exit(0);
}
#endif /* TESTPROG */