linux內存管理概覽

一、內存管理學什麼

Linux內存管理的三個大點:

  1. 虛擬內存(體現對內存的需求)
  2. 內存映射(虛擬內存映射物理內存)
  3. 物理內存(頁面的供應)

 

二、知識點

  • 進程PCB:task_struct
  • 虛擬內存結構:task_struct -> mm_struct -> vm_area_struct
  • 頁表映射:mm_struct -> pgd、分段、分頁、多級頁表、pte(page table entry)
  • 物理內存結構:mem_map、pglist_data、zone、free_area_struct、page
  • 物理內存管理:buddy、slab、kswap、watermark、LRU、active & inactive page
  • 內核棧:void * stack&thread_info、32bit和64bit系統上的差異
  • 缺頁異常:do_page_fault(do_falut, do_anonymous_page, do_swap_page)+pte+swap_entry
  • 頁內型:anno page和file-backed page
  • 交換區:swap_info_struct、swap cache、swap_entry
  • OOM:oom-killer和幾個選項(panic_on_oom、oom_kill_allocating_task、/proc/pid/oom_score_adj)

 

三、概覽

3.1 一般流程

要貫穿Linux整個內存管理的邏輯,起點是進程PCB,即task_struct(創建進程的時候,load_elf_binary會根據可執行文件的ELF格式把程序加載到內存並做好VMA的映射,此時每個進程的內核棧在32bit系統上會默認會分配8KB內存,在64bit系統上會分配16kb內存),其中的mm_struct:

  1. 先分配虛擬內存,等到實際用到物理內存的時候,纔會去分配物理頁(也就是頁表地址映射發現沒PTE的時候),用戶進程通過malloc來分配虛擬內存,如果需要的內存小於128kb,則走brk,大於128kb,走mmap(內核通過vmalloc來分配內核頁表的虛擬內存)
    1. 進程的虛擬地址空間結構
    2. 會分配一個vma_struct
  2. 程序中的邏輯地址經過計算得到線性地址,線性地址從CPU中流出,經過MMU做映射,此時頁表中的PTE有兩種狀態(在CSAPP第九章有介紹,已分配和未分配兩種狀態——PTE值是否爲0,其中已分配又分爲已緩存和未緩存兩種子狀態——PTE值不爲0,但是PTE最後一個bit位存在位P,P=0表示內存頁交換到swap不在內存,P=1表示頁就在內存中)
    1. 已分配物理內存:這種情況有兩種狀態,看最低位P存在位的值
      1. P=0:page不在內存中,而在swap,所以此時頁表中該頁表項存放的不是PTE,而是swap_entry(24bit offset + 6bit type + 1bit present+ 1bit protnone),走swap cache+swap_info_struct(一樣觸發缺頁異常,走do_page_falut -> do_swap_page流程)
      2. P=1:page在內存中,頁表中該頁表項存放的是PTE,高20bit就是頁號,低位12bit補0即爲物理頁起始地址
    2. 未分配物理內存,此時會走page fault,入口do_page_fault -> do_no_page
      1. 看vm_area_struct -> vm_ops中是否有填充或提供nopage()函數(mmap映射的時候,會分配vm_area_struct)
        1. 沒有nopage()函數,則走do_anoymous_page,分配一個匿名頁,填充頁表pte
        2. 有nopage()函數,走do_fault,不同的設備驅動會提供不同的nopage()函數,這時候會從磁盤讀取數據到物理頁,然後填充頁表pte(在做mmap的時候,不同設備驅動中的nopage()函數會關聯到vm_area_struct -> vm_ops中)
  3. 如果內存不夠,此時需要回收內存,一般由kswap內核線程來操作,但是到了water_mark.min,會同步做direct reclaim
    1. 到了watermark_low,喚醒kswap內核線程,回收內存,直到watermark_high
    2. 對於file-backed page,如果頁dirt,則數據寫回磁盤後釋放頁面,如果clean直接釋放;
    3. 對於anno_page,走 swap流程(swap_entry結構在《深入理解Linux虛擬內存》中有講解24bit offset + 6bit type + 1bit present+ 1bit protnone)
    4. 如果到了watermark_min,觸發direct reclaim
  4. 如果走了回收內存流程,還是沒有足夠的內存,那麼走oom流程
    1. 根據panic_on_oom的配置,是走系統崩潰,還是殺進程
    2. 如果殺進程,根據oom_kill_allocating_task配置,是選一個大內存運行時間短的進程殺,還是直接殺觸發oom的進程
    3. 如果選擇殺oom_score進程,可以通過設置/proc/pid/oom_score_adj來配置進程的打分,可以設置-1000~1000,如果設置爲-1000,則oom時永遠不會選擇殺該進程

3.2 內核棧

Linux給每個task都分配了內核棧。在32位系統上arch/x86/include/asm/page_32_types.h,是這樣定義的:一個PAGE_SIZE是4K,左移一位就是乘以2,也就是8K。

#define THREAD_SIZE_ORDER 1 
#define THREAD_SIZE (PAGE_SIZE << THREAD_SIZE_ORDER)

內核棧在64位系統上arch/x86/include/asm/page_64_types.h,是這樣定義的:在PAGE_SIZE的基礎上左移兩位,也即16K,並且要求起始地址必須是8192的整數倍。

#ifdef CONFIG_KASAN
#define KASAN_STACK_ORDER 1
#else
#define KASAN_STACK_ORDER 0
#endif

#define THREAD_SIZE_ORDER	(2 + KASAN_STACK_ORDER)
#define THREAD_SIZE  (PAGE_SIZE << THREAD_SIZE_ORDER)

這段空間的最低位置,是一個thread_info結構。這個結構是對task_struct結構的補充。因爲task_struct結構龐大但是通用,不同的體系結構就需要保存不同的東西,所以往往與體系結構有關的,都放在thread_info裏面。

32bit系統和64bit系統上,內核棧的差異,參看《趣談Linux操作系統》專欄。

 

 

四、缺頁中斷

參看《深入理解Linux虛擬內存管理》4.6章節

 

 

 

 

 

 

 

後記:本文只是休息時寫的流水賬,更多詳情見有道筆記

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章