Linux進程的內存管理之malloc和mmap (有這一文就夠了~)

linux上的碼農 發佈 2022-07-06T06:40:03.644614+00:00

我們知道了進程內存的最小單位是vma,根據不同的用處又劃分了不同類型的vma,比如heap: 動態分配和釋放的內存stack: 存放局部變量和實現函數調用mmap:文件區間映射到虛擬地址空間的內存映射text,data,bss這篇我們就看下進程動態申請的內存,我們知道進程動態申請

我們知道了進程內存的最小單位是vma,根據不同的用處又劃分了不同類型的vma,比如

  • heap: 動態分配和釋放的內存
  • stack: 存放局部變量和實現函數調用
  • mmap:文件區間映射到虛擬地址空間的內存映射
  • text,data,bss

這篇我們就看下進程動態申請的內存,我們知道進程動態申請內存的函數是malloc,這篇講下其涉及到的vma,即heap和mmap。

malloc

在linux標準libc庫種,malloc函數的實現會根據分配內存的size來決定使用哪個分配函數,當size小於等於128KB時,調用brk分配;當size大於128KB時,調用mmap分配內存。size可由M_MMAP_THRESHOLD選項調節。如下圖:



  • sys_brk分配過過程主要是調整brk位置
  • sys_mmap分配過程中主要是在堆和棧中間(memory mapping segment)找一段空閒的虛擬內存


更多linux內核視頻教程文檔資料免費領取後台私信【內核】自行獲取.

Linux內核源碼/內存調優/文件系統/進程管理/設備驅動/網絡協議棧-學習視頻教程-騰訊課堂

brk

堆內存是由低地址向高地址方向增長。分配內存時,將heap段的最高地址指針mm->brk往高地址擴展。釋放內存時,把mm->brk向低地址收縮。


完成這段申請後,只是開闢了一段區域,通常還不會立馬分配物理內存,物理內存的分配會發生在訪問時出現缺頁異常後再處理,這個後續文章咱們再進一步分析。

SYSCALL_DEFINE1(brk, unsigned long, brk)
{
......
 //都需要頁對齊,方便映射,mm->brk可以理解為end_brk,即當前進程堆的末尾
 newbrk = PAGE_ALIGN(brk);
 oldbrk = PAGE_ALIGN(mm->brk);
 if (oldbrk == newbrk)
  goto set_brk;

 /* Always allow shrinking brk. */
 if (brk <= mm->brk) {
  //對heap收縮,調用free就會滿足這個條件,減少堆,執行unmap
  if (!do_munmap(mm, newbrk, oldbrk-newbrk, &uf))
   goto set_brk;
  goto out;
 }

 /* Check against existing mmap mappings. */
 next = find_vma(mm, oldbrk);
 if (next && newbrk + PAGE_SIZE > vm_start_gap(next))
  goto out;

 /* Ok, looks good - let it rip. */
 //對heap擴展,是brk函數的核心,裡面創建一個vma,然後instert全局鍊表中
 if (do_brk_flags(oldbrk, newbrk-oldbrk, 0, &uf) < 0)
  goto out;

set_brk: //設置這次請求的brk到進程描述符mm->brk中
 mm->brk = brk;
 populate = newbrk > oldbrk && (mm->def_flags & vm_LOCKED) != 0;
 up_write(&mm->mmap_sem);
 userfaultfd_unmap_complete(mm, &uf);
 if (populate)
  mm_populate(oldbrk, newbrk - oldbrk);
 return brk;

out:
 retval = mm->brk;
  //釋放信號量
 up_write(&mm->mmap_sem);
 return retval;
}


大概流程整理如下:


mmap

  • 私有匿名映射:通常用於內存分配,堆,棧
  • 共享匿名映射:通常用於進程間共享內存,在內存文件系統中創建/dev/zero設備
  • 私有文件映射:通常用於加載動態庫,代碼段,數據段
  • 共享文件映射:通常用於文件讀寫和進程間通信


unsigned long do_mmap(struct file *file, unsigned long addr,
   unsigned long len, unsigned long prot,
   unsigned long flags, vm_flags_t vm_flags,
   unsigned long pgoff, unsigned long *populate,
   struct list_head *uf)
{
 ......
 //獲取未映射區域
 addr = get_unmapped_area(file, addr, len, pgoff, flags);
 if (offset_in_page(addr))
  return addr;

 addr = mmap_region(file, addr, len, vm_flags, pgoff, uf);
 ......
 return addr;
}

整理流程如下:

關鍵字: