linux内核malloc内存分配
内核kernel/malloc.c中的内存分配函数malloc,与用户程序使用的malloc不同,内核0.98版以后改名为kmalloc()
对于应用程序使用的名称相同的内存分配函数一般在开发环境的函数库文件中实现,例如gcc环境中的libc.a库。由于开发环境中的库函数本身链接于用户程序中,因此他们不能直接使用内核中的get_free_page()等函数来实现内存分配函数
他们也没有直接管理内存页面的必要,因为函数库libc.a中的内存分配函数只要按照程序请求动态调整进程数据段末尾的设定值,并且不覆盖逻辑地址空间末端的堆栈和环境参数即可,剩下的具体内存映射均由内核完成
这种调整进程数据段末端位置的操作即是库中内存分配函数的主要功能,并且需要调用内核系统调用brk()
int sys_brk(unsigned long end_data_seg)
{
if (end_data_seg >= current->end_code &&
end_data_seg < current->start_stack - 16384)
current->brk = end_data_seg;
return current->brk;
}
因此若能查看开发环境中库函数实现的源代码,你将发现其中的 malloc()、calloc()等内存分配函数除了在管理着进程动态申请的内存区域以外,最终仅调用了内核系统调用 brk()
开发环境中库中的内存分配函数与这里的内核库中的分配函数相同之处仅在于它们都需要对已分配内存空间进行动态管理。采用的管理方法基本是一样的。
malloc()函数使用了存储桶的原理对分配的内存进行管理,基本思想是对请求的不同内存块大小,使用存储桶目录分别进行处理
比如对于请求内存块的长度在 32 字节或 32 字节以下但大于 16 字节时,就使用存储桶目录第二项对应的存储桶描述符链表分配内存块
在第一次调用malloc()函数时,首先要建立一个页面的空闲存储桶描述符链表,其中存放着还未使用或已经使用完毕而回收的描述符
free_bucket_desc是链表头指针,从链表中取出或放入一个描述符都是从链表头开始操作。当取出一个描述符时,将链表头指针指向的头一个描述符取出;当释放一个空闲描述符时,也是将其放在链表头处
在系统运行过程中,如果某一时刻所有桶描述符都已占用,那么free_bucket_desc就会位NULL,因此在没有桶描述符被释放的前提下,下一次需要使用空闲桶描述符时,程序会再次申请一个页面并在其上新建一个空闲存储桶描述符链表
free_s()函数用于回收用户释放的内存块。基本方式是首先根据内存块的地址换算出对应页面的地址(用页面长度进行模运算),然后搜索目录中的所有描述符,找到对应该页面的描述符
将该释放的内存块链入freeptr所指向的空闲对象链表中,并将描述符的对象引用计数值减1.如果引用计数值此时等于0,则表示该描述符对应的页面已经完全空出,可以释放该内存页面并将该描述符收回到空闲描述符链表中
void free_s(void* obj, int size){
void *page;
struct _bucket_dir *bdir;
struct bucket_desc *bdesc, *prev;
page = (void*)((unsigned long)obj & 0xfffff000);
for(bddir=bucket_dir; bdir->size; bdir++){
prev = 0;
if(bdir->size < size)
continue;
for(bdesc=bdir->chain; bdesc; bdesc=bdesc->next){
if(bdesc->page==page)
goto found;
prev = bdesc;
}
}
panic("Bad address passed to kernel free_s()");
found:
cli();
//将对象内存块链入空闲块对象链表里
*((void**)obj) = bdesc->freeptr;
bdesc->freeptr = obj;
bdesc->refcnt--;
//引用计数为0 释放对应内存页面和该桶描述符
if(bdesc->refcnt==0){
//如果prev不是搜索到的描述符的前一个,重新搜索当前描述符的前一个描述符
if((prev && (prev->next!=bdesc)) || (!prev && (bdir->chain != bdesc))){
for(prev=bdir->chain; prev; prev=prev->next){
if(prev->next==bdesc)
break;
}
}
if(prev)
prev->next = bdesc->next;
else{
if(bdir->chain != bdesc)
panic("malloc bucket chains corrupt");
bdir->chain = bdesc->next;
}
free_page((unsigned long)bdesc->page);
bdesc->next = free_bucket_desc;
free_bucket_desc = bdesc;
}
sti();
return;
}
linux内存管理可参考之前的记录:
内核内存管理juejin.cn/post/740391…
用户态内存管理: juejin.cn/post/739728…
《linux内核完全注释》