6.堆的管理

堆是一个连续的内存区域,在扩展时自下至上增长。的mm_struct结构,包含了堆在虚拟地址空间中的起始和当前结束地址(start_brk和brk)。
<mm_types.h> 
struct mm_struct 
{ 
... 
  unsigned long start_brk, brk, start_stack; 
... 
};
brk系统调用只需要一个参数,用于指定堆在虚拟地址空间中新的结束地址(如果堆将要收缩,当然可以小于当前值)。照例,brk系统调用实现的入口是sys_brk函数,其代码流程图在图4-16给出。

 

 

brk机制不是一个独立的内核概念,而是基于匿名映射实现,以减少内部的开销。因此前几节讨论的许多用于管理内存映射的函数,都可以在实现sys_brk时重用。
在检查过用作brk值的新地址未超出堆的限制之后,sys_brk第一个重要操作是将请求的地址按页长度对齐。
mm/mmap.c 
asmlinkage unsigned long sys_brk(unsigned long brk) 
{ 
  unsigned long rlim, retval; 
  unsigned long newbrk, oldbrk; 
  struct mm_struct *mm = current->mm; 
... 
  newbrk = PAGE_ALIGN(brk); 
  oldbrk = PAGE_ALIGN(mm->brk); 
...
该代码确保brk的新值(原值也同样)是系统页长度的倍数。换句话说,一页是用brk能分配的最小内存区域
在需要收缩堆时将调用do_munmap,我们在4.对区域的操作已经熟悉该函数。
<mm/mmap.c> 
  /* 总是允许缩减brk。 */
  if (brk <= mm->brk) { 
    if (!do_munmap(mm, newbrk, oldbrk-newbrk)) 
      goto set_brk; 
    goto out; 
  } 
...
如果堆将要扩大,内核首先必须检查新的长度是否超出进程的最大堆长度限制。find_vma_intersection接下来检查扩大的堆是否与进程中现存的映射重叠。倘若如此,则什么也不做,立即返回。
<mm/mmap.c> 
/* 检查是否与现存的mmap映射冲突。 */ 
if (find_vma_intersection(mm, oldbrk, newbrk+PAGE_SIZE)) 
  goto out; 
...
否则将扩大堆的实际工作委托给do_brk。函数总是返回mm->brk的新值,无论与原值相比是增大、缩小、还是不变。
<mm/mmap.c> 
/* 可以,看起来不错,不要担心。*/ 
  if (do_brk(oldbrk, newbrk-oldbrk) != oldbrk) 
    goto out; 
  set_brk: 
    mm->brk = brk; 
  out: 
    retval = mm->brk; 
  return retval; 
}
 
我们不需要单独讨论do_brk,因为实质上它是do_mmap_pgoff的简化版本,没什么新东西。与后者类似,它在用户地址空间中创建了一个匿名映射,但省去了一些安全检查和用于提高代码性能的对特殊情况的处理。
posted @ 2022-03-26 21:17  while(true);;  阅读(39)  评论(0编辑  收藏  举报