do_brk()源码剖析
unsigned long do_brk(unsigned long addr, unsigned long len){...} do_brk函数目的是为了将addr位置向后继续申请len字节长度,用作于扩展堆内存的长度
-
首先会对于len这个长度进行页面对齐,并且去判断页面对齐之后是否超出了边界
len = PAGE_ALIGN(len); if (!len) return addr; if ((addr + len) > TASK_SIZE || (addr + len) < addr) return -EINVAL;
-
判断一下当前的物理内存是否处于锁定状态(防止被交换出去),保持和原来状态的一致性
if (mm->def_flags & VM_LOCKED) { unsigned long locked, lock_limit; locked = mm->locked_vm << PAGE_SHIFT; lock_limit = current->signal->rlim[RLIMIT_MEMLOCK].rlim_cur; locked += len; if (locked > lock_limit && !capable(CAP_IPC_LOCK)) return -EAGAIN; }
-
寻找当前addr位置对应的vma,看看是不是已经存在的vma将addr已经覆盖了,需要调用munmap将覆盖部分清除,保证addr到addr+len这一段内存是空白的没有被任何的vma所覆盖。
vma = find_vma_prepare(mm, addr, &prev, &rb_link, &rb_parent); if (vma && vma->vm_start < addr + len) { if (do_munmap(mm, addr, len)) return -ENOMEM; goto munmap_back; }
-
对于当前上一个的vma进行扩展,扩展至addr加上len的位置,如果成功那么OK,进行mm_struct的维护然后返回。
if (vma_merge(mm, prev, addr, addr + len, flags, NULL, NULL, pgoff, NULL)) goto out;
-
否则需要来创建一个新的vma来对当前的内存进行映射,将变量设置后放入vma的红黑树链表中。
vma = kmem_cache_alloc(vm_area_cachep, SLAB_KERNEL); if (!vma) { vm_unacct_memory(len >> PAGE_SHIFT); return -ENOMEM; } memset(vma, 0, sizeof(*vma)); vma->vm_mm = mm; vma->vm_start = addr; vma->vm_end = addr + len; vma->vm_pgoff = pgoff; vma->vm_flags = flags; vma->vm_page_prot = protection_map[flags & 0x0f]; vma_link(mm, vma, prev, rb_link, rb_parent);