brk系統呼叫實現分析
阿新 • • 發佈:2019-01-22
brk(addr)直接修改堆的大小。addr指定current->mm->brk的新值,返回值是線性區新的結束地址,這是一個系統呼叫。當用戶態的程序呼叫brk()系統呼叫時,核心執行sys_brk(addr)函式。下面分析這個函式的執行流程:
1:檢測addr引數是否位於程序程式碼段所在的線性區,如果是直接返回,因為堆不能與程序程式碼段所在的線性區重合。
- mm=current->mm;
- down_write(&mm->mmap_sem);
- if(addr<mm->end_code){
- out:
- up_write(&mm->mmap_sem);
- return mm->brk;
- }
2:由於brk系統呼叫作用於一個線性區,它分配和釋放完整的頁。因此,該函式把addr的值調整為PAGE_SIZE的倍數,然後把調整的結果和記憶體描述的brk程序比較。
- newbrk=(addr+0xfff)&0xfffff000;
- oldbrk=(mm->brk+0xfff)&0xfffff000;
- if(oldbrk==newbrk)
- {
- mm->brk=addr;
- goto out;
- }
3:如果程序請求縮小堆,則sys_brk()呼叫do_munmap()完成這項任務,然後返回
- if(addr<=mm->brk)
- {
- if(!do_munmap(mm,newbrk,oldbrk-newbrk))
- mm->brk=addr;
- goto out;
- }
4:如果程序請求擴大堆,則sys_brk首先檢查是否允許程序這麼做。如果程序企圖分配在其限制範圍之外的記憶體,函式並不多分配記憶體,只簡單返回mm->brk的原有值
- rlim=current->signal->rlim[RLIMIT_DATA].rlim_cur;
- if(rlim<RLIM_INFINITY & addr - mm->start_data>rlim)
- goto out;
5:然後,函式檢查擴大之後的堆是否和程序的其他線性區重疊,如果是,不做任何事情就返回:
- if(find_vma_itersection(mm,oldbrk,newbrk+PAGE_SIZE))
- goto out;
6:如果一切都順利,則呼叫do_brk()函式。如果返回oldbrk,則分配成功且sys_brk返回addr的值,否則返回舊的mm->brk
- if(do_brk(oldbrk,newbrk-oldbrk)==oldbrk)
- mm->brk=addr;
- goto out;
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。