【linux kernel】虚拟内存分配

一、用户空间虚拟内存的分配
【接口】
1)mmap()函数
2)malloc()函数


1.mmap()
->sys_mmap() //sys.c
->sys_mmap_pgoff() //见SYSCALL_DEFINE6(mmap_pgoff, unsigned long, addr, unsigned long, len,...) , mm/mmap.c
->vm_mmap_pgoff()
->do_mmap_pgoff()
->get_unmapped_area(struct file *file, unsigned long addr,...)
{
get_area = current->mm->get_unmapped_area; //默认是 arch_get_unmapped_area_topdown()
if (file && file->f_op && file->f_op->get_unmapped_area)
get_area = file->f_op->get_unmapped_area; //如果是文件映射且定义了具体的unmap方法,则使用该方法
addr = get_area(file, addr, len, pgoff, flags);
}

unsigned long arch_get_unmapped_area_topdown(struct file *filp, const unsigned long addr0,
 const unsigned long len, const unsigned long pgoff,
 const unsigned long flags)
{
struct vm_area_struct *vma;
struct mm_struct *mm = current->mm;
unsigned long addr = addr0;
struct vm_unmapped_area_info info;


/* requested length too big for entire address space */
if (len > TASK_SIZE - mmap_min_addr) //防止mapsize过大以至于大于3G
return -ENOMEM;


if (flags & MAP_FIXED)
return addr;


/* requesting a specific address */
if (addr) {
addr = PAGE_ALIGN(addr);
vma = find_vma(mm, addr); //find_vma()会查找当前进程mm的VMA信息的RB tree (mm->mm_rb) 
if (TASK_SIZE - len >= addr && addr >= mmap_min_addr &&
(!vma || addr + len <= vma->vm_start)) //如果地址addr没有被映射,且当前进程的mmap区域(mmap_min_addr~TASK_SIZE)足够大,那么直接返回用户请求的用户虚拟地址
return addr;
}


info.flags = VM_UNMAPPED_AREA_TOPDOWN;
info.length = len;
info.low_limit = max(PAGE_SIZE, mmap_min_addr);
info.high_limit = mm->mmap_base;
info.align_mask = 0;
addr = vm_unmapped_area(&info);   //查找mm->mm_rb中处于[low_limit~high_limit]范围的unmaped area


/*
* A failed mmap() very likely causes application failure,
* so fall back to the bottom-up function here. This scenario
* can happen with large stack limits and large mmap()
* allocations.
*/
if (addr & ~PAGE_MASK) {
VM_BUG_ON(addr != -ENOMEM);
info.flags = 0;
info.low_limit = TASK_UNMAPPED_BASE;  //一般是1G处(0x40000000)
info.high_limit = TASK_SIZE; //3G处(0xC0000000)
addr = vm_unmapped_area(&info); //查找mm->mm_rb中处于[low_limit~high_limit]范围的unmaped area
}


return addr;
}


2. malloc()
->sys_brk() //SYSCALL_DEFINE1(brk, unsigned long, brk), mm/mmap.c
->do_brk() //do_brk(addr, len)函数给从addr到addr+len建立虚拟内存区vm_area_struct(该区的起始地址为addr,结束地址为addr+len),该虚拟内存区作为进程的堆来使用。
->get_unmapped_area(NULL, addr, len, 0, MAP_FIXED)
->mm->brk = brk;

malloc()在Linux上的基本实现是内核的brk系统调用。brk()是一个非常简单的系统调用,只是简单地改变mm_struct结构的成员变量brk的值。
内核数据结构mm_struct中的成员变量start_code和end_code是进程代码段的起始和终止地址,start_data和 end_data是进程数据段的起始和终止地址,
start_stack是进程堆栈段起始地址,start_brk是进程动态内存分配起始地址(堆的起始地址), brk(堆的当前最后地址),就是动态内存分配当前的终止地址。





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章