brk系统调用实现分析

brk(addr)直接修改堆的大小。addr指定current->mm->brk的新值,返回值是线性区新的结束地址,这是一个系统调用。当用户态的进程调用brk()系统调用时,内核执行sys_brk(addr)函数。下面分析这个函数的执行流程:

1:检测addr参数是否位于进程代码段所在的线性区,如果是直接返回,因为堆不能与进程代码段所在的线性区重合。

  1. mm=current->mm;  
  2. down_write(&mm->mmap_sem);  
  3. if(addr<mm->end_code){  
  4. out:  
  5.     up_write(&mm->mmap_sem);  
  6.     return mm->brk;  
  7. }  

2:由于brk系统调用作用于一个线性区,它分配和释放完整的页。因此,该函数把addr的值调整为PAGE_SIZE的倍数,然后把调整的结果和内存描述的brk进程比较。

  1. newbrk=(addr+0xfff)&0xfffff000;  
  2. oldbrk=(mm->brk+0xfff)&0xfffff000;  
  3. if(oldbrk==newbrk)  
  4. {  
  5.     mm->brk=addr;  
  6.     goto out;  
  7. }  

3:如果进程请求缩小堆,则sys_brk()调用do_munmap()完成这项任务,然后返回

  1. if(addr<=mm->brk)  
  2. {  
  3.     if(!do_munmap(mm,newbrk,oldbrk-newbrk))  
  4.         mm->brk=addr;  
  5.     goto out;  
  6. }  

4:如果进程请求扩大堆,则sys_brk首先检查是否允许进程这么做。如果进程企图分配在其限制范围之外的内存,函数并不多分配内存,只简单返回mm->brk的原有值

  1. rlim=current->signal->rlim[RLIMIT_DATA].rlim_cur;  
  2. if(rlim<RLIM_INFINITY & addr - mm->start_data>rlim)  
  3.     goto out;  

5:然后,函数检查扩大之后的堆是否和进程的其他线性区重叠,如果是,不做任何事情就返回:

  1. if(find_vma_itersection(mm,oldbrk,newbrk+PAGE_SIZE))  
  2.     goto out;  

6:如果一切都顺利,则调用do_brk()函数。如果返回oldbrk,则分配成功且sys_brk返回addr的值,否则返回旧的mm->brk

  1. if(do_brk(oldbrk,newbrk-oldbrk)==oldbrk)  
  2.     mm->brk=addr;  
  3. goto out;  
do_brk()函数实际上是仅处理匿名线性区的do_mmap()简化版。可以认为它的调用等价于:

do_mmap(NULL,oldbrk,newbrk-oldbrk,PROT_READ|PROT_WRITE|PROT_EXEC,MAP_FIXED|MAP_PRIVATE,0)

当然do_brk()比do_mmap()稍快,因为前者假定线性区不映射磁盘上的文件,从而避免了检查线性区对象的几个字段。在这里就不介绍do_brk()函数了,因后面会写一篇专门介绍重要的do_munmap。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值