首先简单说一下标准库中malloc实现原理:
标准库内部通过一个双向链表,管理在堆中动态分配的内存。
malloc函数分配内存时会附加若干(通常是12个)字节,存放控制信息。
该信息一旦被意外损坏,可能在后续操作中引发异常。
mmap/munmap 底层不维护任何东西,只是返回一个首地址,所分配内存位于堆中。
brk/sbrk 底层维护一个指针,记录所分配的内存结尾,所分配内存位于堆中,底层调用mmap/munmap。
malloc 底层维护一个双向链表和必要的控制信息,不可越界访问,所分配内存位于堆中,底层调用brk/sbrk。
每个进程都有4G的虚拟内存空间,虚拟内存地址只是一个数字,并没有和实际的物理内存相关联。
所谓内存分配与释放,其本质就是建立或取消虚拟内存和物理内存间的映射关系。
几个用到的函数:
void*
sbrk (
intptr_t increment // 内存增量(以字节为单位)
);
返回上次调用brk/sbrk后的末尾地址,失败返回-1。
increment取值:
0 - 获取末尾地址。
>0 - 增加内存空间。
<0 - 释放内存空间。
内部维护一个指针,指向当前堆内存最后一个字节的下一个位置。
sbrk函数根据增量参数调整该指针的位置,同时返回该指针原来的位置。
若发现页耗尽或空闲,则自动追加或取消页映射。
int
brk (
void* end_data_segment // 内存块末尾地址
);
成功返回0,失败返回-1。
内部维护一个指针,指向当前堆内存最后一个字节的下一个位置。
brk函数根据指针参数设置该指针的位置。
若发现页耗尽或空闲,则自动追加或取消页映射。
sbrk/brk底层维护一个指针位置,以页(4K)为单位分配和释放虚拟内存。
简便起见,可用sbrk分配内存,用brk释放内存。
#include <stdio.h> #include <unistd.h> #include <stdbool.h> typedef struct mem_control_block //内存控制块 { bool free; //自由标志 struct mem_control_block* prev; //前向指针 size_t size; //块大小 }MCB; MCB* g_top; //内存栈顶指针 // +----------------------+ g_top // v | | // +------+------------+--|---+------------+------+------------+ // | prev | | prev | | prev | | // | free | | free | | free | | // | size | | size | | size | | // +------+------------+------+------------+------+------------+ // MCB |<-- size -->| void* my_malloc(size_t size) { MCB* mcb; for(mcb = g_top; mcb; mcb = mcb->prev) if(mcb->free && mcb->size >= size)//寻找可用空块 break; if(!mcb) //如果没有可用空块 { mcb = sbrk(sizeof(MCB) + size); //增量分配size大小内存 if(mcb == (void*)-1) //如果分配失败,打印错误信息 { perror("sbrk"); return NULL; } mcb->prev = g_top; //调整前向指针 mcb->size = size; //大小 g_top = mcb; //调整栈顶指针 } mcb->free = false; //无论用原来空块还是新分配的块,都标记为不可用 return mcb + 1; //返回实际分配的内存起始地址 } void my_free(void* ptr) { if(!ptr) return; MCB* mcb = (MCB*)ptr - 1; //取控制块起始地址, //注意:ptr为实际可用内存起始地址 mcb->free = true; //块标记为可用 //在栈中查找连续内存块 for(mcb = g_top; mcb->prev; mcb = mcb->prev) if(!mcb->free) break; //释放整个栈所有内存块 if(mcb->free) { g_top = mcb->prev; brk(mcb); //修改内存块末尾地址 } else //释放连续的标记为true的内存块 { g_top = mcb; brk((void*)mcb + sizeof(MCB) + mcb->size); } } //测试 int main(void) { int* pa[10]; size_t size = sizeof(pa) / sizeof(pa[0]), i, j; for(i = 0; i < size; ++i) { if(!(pa[i] = (int*)my_malloc((i+1) * sizeof(int)))) { perror("my_malloc"); return -1; } for(j = 0; j <= i; ++j) pa[i][j] = j; } for(i = 0; i < size; ++i) { for(j = 0; j <= i; ++j) printf("%d ", pa[i][j]); printf("\n"); } for(;;) { my_free(pa[--i]); if(! i) break; } return 0; }