Linux 內存源碼初步閱讀

幾個數據結構

  1. pg_data_t
    這個數據結構代表每一個NUMA節點,關於NUMA可以稍微看看這篇瞭解下
  2. zonelist
    在這裏插入圖片描述
    內存域的列表,按照內存域的優先級排列。
  3. zone
    內存域,內存域常見類型有幾種:
    在這裏插入圖片描述
  4. page
    代表一個物理內存頁,頁幀。
  5. free_area
    這個跟夥伴內存分配相關。
    在這裏插入圖片描述
    放一個自己畫的圖
    在這裏插入圖片描述
    從上面的圖裏面大致可以反映出代碼的結構,對照代碼看的話更清晰一些。

初始化內存

直接貼書上的圖吧
在這裏插入圖片描述

  1. setup_arch: 函數內部會去獲取機器內存信息,這個跟特定的機器相關,我看到好多都是e820,關於e820可以參照WiKi這篇博客
    在這裏插入圖片描述
  2. setup_per_cpu_areas:拷貝一些變量到每個CPU
  3. build_all_zonelists: 構建每個內存區域的優先級,分配內存的時候按照這個優先級去zone裏面找。
  4. setup_per_cpu_pageset: 初始化每個CPU的pageset

夥伴系統

初始化

把系統的可用內存跟數據結構關聯起來的函數是:free_area_init_nodes
在這裏插入圖片描述
對於每個NUMA節點,調用了free_area_init_node,這個函數裏面調用calculate_node_totalpages計算這個節點頁的總量,調用alloc_node_mem_map來初始化struct page所需要的內存並初始化mem_map。最後再調用free_area_init_core,這個函數裏面需要關注下面三個函數:

  1. zone_pcp_init:初始化這個內存域的per-CPU緩存
  2. init_currently_empty_zone:初始化free_area
  3. memmap_init這個是個宏,後面的函數是memmap_init_zone
  4. memmap_init_zone:這個函數裏面會調用:
    1. pfn_to_page:物理頁面序號轉換成頁幀
    2. set_page_links:設置這個頁屬於哪個內存域,屬於哪個NUMA節點。
    3. SetPageReserved:設置這個頁成Reserved。

夥伴系統分配

在這裏插入圖片描述
所有的函數最後都歸到alloc_pages_node這個函數,這個函數又會調用__alloc_pages_nodemask這個函數,這個函數就是核心分配了,內部主要調用了兩個函數:

get_page_from_freelist
__alloc_pages_slowpath

第一個函數先檢查內存水位線,再嘗試分配合適的內存,如果可以分配內存,那麼調用buffered_rmqueue這個函數獲取頁,然後就結束分配。
如果第一次嘗試不成功的話,會調用下面那個函數,下面這個就相當於進入了分配的“slowpath”,slowpath裏面會先喚醒kswapd進程看看有沒有能交換到swap的頁,然後再調用第一個函數去分配,這次會比第一次積極一些,會放寬一些條件限制。

buffered_rmqueue這個函數會檢查這些可分配的頁是否是連續的,然後再移除這些頁並重排內存區。
__rmqueue這個函數會調用 __rmqueue_smallest來獲取指定節點,指定內存域,指定階數,指定遷移類型的頁。最後調用expand函數重排內存區。

好了,經過這一系列的函數,已經獲取了特定數量的頁了。

slab/slub/slob

通過malloc分配用戶空間內存的時候,頁單位太大,需要把頁分成更小的單位來管理。
slab:默認分配器
slub:針對大型計算機的分配器
在這裏插入圖片描述
slab裏面關鍵的概念就是“緩存”和“對象”。

  1. 緩存
    對應的數據結構是kmem_cache,每種對應大小的對象都對應一個kmem_cache,相當於一個籃子。
  2. 對象
    籃子裏面的果子。
    在這裏插入圖片描述

分配對象步驟

在這裏插入圖片描述
這篇博文很好。

總結

獲取內存佈局[e820],並初始化頁幀 --> 頁的分配[夥伴系統] --> 小對象的分配[slab系列]

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