公司的项目都没有自己的内存管理机制,要使用内存时就malloc,不用时就free,感觉有效率问题,而且有内存泄漏的隐患,因此我决定设计一套给嵌入式linux设备使用的内存管理机制(很大部分参考了某开源web服务器中的设计)。
设计思想综述
用Alloc结构体表示一个内存块,内存块的大小有13个等级,每个等级分别是:
16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536字节。
#define MAX_CLASS 13
typedef struct Alloc {
union {
void *next; /**< Pointer to next in q */
int size; /**< Actual requested size */
} u;
int flags; /**< Per block allocation flags */
} Alloc;
上面的结构体中有个指针next,用于将所有相同大小的内存块串成链表。
13种大小的内存块,就意味着有13条链表,链表首元素存放在全局数组中:
static Alloc *qhead[MAX_CLASS]; /* Per class block q head */
对于大小在64k以内的内存需求,均到我们预先分配的内存块去取,除非内存块没有空闲了。
64k以上的内存需求,直接用malloc分配。
实际实现:
#define MAX_CLASS 13
typedef struct Alloc {
union {
void *next; /**< Pointer to next in q */
int size; /**< Actual requested size */
} u;
int flags; /**< Per block allocation flags */
} Alloc;
//对齐函数,如果size能被4整除,返回size,否则返回下一个比size大的能被4整除的数,在内存分配时用到
#define ROUNDUP4(size) ((size) % 4) ? (size) + (4 - ((size) % 4)) : (size)
#define USE_MALLOC 0x1 /**< Okay to use malloc if required */
#define USER_BUF 0x2 /* User supplied buffer for mem */
#define INTEGRITY 0x8124000 /* Integrity value */
#define INTEGRITY_MASK 0xFFFF000 /* Integrity mask */
#define DEFAULT_MEM (64 * 1024) /**< Default memory allocation */
#define SHIFT 4 /**< Convert size to class */
#define ROUND ((1 << (B_SHIFT)) - 1)
#define MALLOCED 0x80000000 /* Block was malloced */
#define FILL_CHAR (0x77) /* Fill byte for buffers */
#define FILL_WORD (0x77777777) /* Fill word for buffers */
static Alloc *qhead[MAX_CLASS]; /* Per class block q head */
static char *freeBuf; /* Pointer to free memory */
static char *freeNext; /* Pointer to next free mem */
static int freeSize; /* Size of free memory */
static int freeLeft; /* Size of free left for use */
static int controlFlags = WEBS_USE_MALLOC; /* Default to auto-malloc */
static int openCount = 0; /* 使用该片内存的进程数 */
typedef void (*MemNotifier)(ssize size);
static MemNotifier memNotifier;//留给用户的内存分配通知函数,见下面
//该函数必须在程序启动后就调用,参数buf不为NULL,则表示使用用户提供的buf作为系统内存,buf为null则会用malloc来分配系统内存。
int openAlloc(void *buf, int bufsize, int flags)
{
controlFlags = flags;
/*
If openAlloc already called by a shared process, just increment the count and return
*/
if (++openCount > 1) {
return 0;
}
if (buf == NULL) {
if (bufsize == 0) {
bufsize = DEFAULT_MEM;//没有指定内存大小时,默认分配64k最为系统的内存
}
bufsize = ROUNDUP4(bufsize);
if ((buf = malloc(bufsize)) == NULL) {
/*
Resetting wopenCount so client code can decide to call wopenAlloc() again with a smaller memory request.
*/
--openCount;
return -1;
}
} else {
controlFlags |= USER_BUF;
}
freeSize = freeLeft = bufsize;
freeBuf = freeNext = buf;
memset(qhead, 0, sizeof(qhead));
return 0;
}
//该函数必须在程序退出之前调用
void closeAlloc()
{
if (--openCount <= 0 && !(controlFlags & USER_BUF)) {
free(freeBuf);
openCount = 0;
}
}
接下来就是重要的内存分配函数了。
该函数是用于计算你需要的内存字节数size对应我们前面定义的13个等级中的哪一个等级
static int allocGetSize(int size, int *q)
{
int mask;
mask = (size == 0) ? 0 : (size-1) >> SHIFT;
for (*q = 0; mask; mask >>= 1) {
*q = *q + 1;
}
return ((1 << (SHIFT + *q)) + sizeof(Alloc));
}
void *alloc(int size)
{
Alloc *bp;
int q, memSize;
/*
Call openAlloc with default values if the application has not yet done so
*/
if (freeBuf == NULL) {
if (openAlloc(NULL, DEFAULT_MEM, 0) < 0) {
return NULL;
}
}
if (size < 0) {
return NULL;
}
memSize = allocGetSize(size, &q);//计算该大小对应哪个等级
//超过了我们定义的等级,也就是说size>=65535
if (q >= MAX_CLASS) {
/*
Size if bigger than the maximum class. Malloc if use has been okayed
*/
if (controlFlags & USE_MALLOC) {
memSize = ROUNDUP4(memSize);
bp = (Alloc*) malloc(memSize);
if (bp == NULL) {
if (memNotifier) {
(memNotifier)(memSize);
}
return NULL;
}
} else {
if (memNotifier) {
(memNotifier)(memSize);
}
return NULL;
}
/*
the u.size is the actual size allocated for data
*/
bp->u.size = memSize - sizeof(Alloc);
bp->flags = MALLOCED;
} else if ((bp = qhead[q]) != NULL) {//取出链表的第一个空闲内存块
/*
Take first block off the relevant q if non-empty
*/
qhead[q] = bp->u.next;//更新头结点
bp->u.size = memSize - sizeof(Alloc);
bp->flags = 0;
} else {
if (freeLeft > memSize) {
/*
The q was empty, and the free list has spare memory so create a new block out of the primary free block
*/
bp = (Alloc*) freeNext;
freeNext += memSize;
freeLeft -= memSize;
bp->u.size = memSize - sizeof(Alloc);
bp->flags = 0;
} else if (controlFlags & USE_MALLOC) {
/*
Nothing left on the primary free list, so malloc a new block
*/
memSize = ROUNDUP4(memSize);
if ((bp = (Alloc*) malloc(memSize)) == NULL) {
if (memNotifier) {
(memNotifier)(memSize);
}
return NULL;
}
bp->u.size = memSize - sizeof(Alloc);
bp->flags = MALLOCED;
} else {
if (memNotifier) {
(memNotifier)(memSize);
}
return NULL;
}
}
bp->flags |= INTEGRITY;
return (void*) ((char*) bp + sizeof(Alloc));
}
//重新为一个内存块分配空间
void *wrealloc(void *mp, int newsize)
{
Alloc *bp;
void *newbuf;
if (mp == NULL) {
return walloc(newsize);
}
bp = (Alloc*) ((char*) mp - sizeof(Alloc));
assert((bp->flags & INTEGRITY_MASK) == INTEGRITY);
/*
If the allocated memory already has enough room just return the previously allocated address.
*/
if (bp->u.size >= newsize) {
return mp;
}
if ((newbuf = walloc(newsize)) != NULL) {
memcpy(newbuf, mp, bp->u.size);
wfree(mp);
}
return newbuf;
}