Linux記憶體管理複習總結

FreeeLinux發表於2017-02-28

首先來看一張圖:

這張圖就是Linux分配記憶體的大致流程。下面我來總結一下。

分頁

首先Linux是基於記憶體管理採用分頁機制,核心程式碼中它將所有段基址都設定為0,Linux採用這樣的方法直接避過了分段機制。僅僅用分段來控制使用者態和使用者態的訪問許可權。

核心記憶體分配

核心對於大塊記憶體的分配基於夥伴演算法,用來解決外部碎片的問題。夥伴演算法最低分配的單位是一頁。在實體記憶體的每個Zone中都有一個free_area[]陣列,分別儲存2的冪的頁面。夥伴演算法就是比如說申請8個頁面,然後首先去查詢free_area[3]有沒有可用的8個頁面,如果沒有就將上一級16個頁面一分為二,一部分分配出來,另一部分重新加入到free_area陣列對應8個頁面的位置。當記憶體釋放時,這兩個8個頁面又會重新合併。(這種互逆的操作將最大力度減小碎片的產生)。

夥伴演算法是基於一頁分配的,核心中對於遠小於一頁的位元組級別記憶體,比如task_struct結構體,使用夥伴演算法就會產生內部碎片。

為了解決內部碎片,Linux引入了slab機制。

slab機制

核心通常會對某些小物件頻繁進行分配,slab分配器通過對類似物件大小的快取提供這個功能。

slab分配器類似於一個全域性的物件池。可以快取不同大小的物件,最小一個快取行大小,最大則是128k。slab分配器有三層結構,分別是per-CPU的快取物件,CPU間共享的快取物件,每個NUMA節點共享的slab頁面。為了避免自旋鎖的競爭,核心分配快取物件優先從per-CPU陣列中獲取,並且獲取陣列最熱的物件。因為最熱的物件可能還在L1 cache中。如果per-CPU陣列中沒有物件分配,則回去CPU共享的shead陣列中查詢物件,如果還沒有,則會去針對NUMA節點的slab三個連結串列中查詢。這三個連結串列是slab滿連結串列,slab部分滿連結串列,slab空連結串列。分配物件從slab部分滿連結串列中查詢,如果部分滿連結串列中所有物件都分配出去了,則會將它加入slab滿連結串列之中。如果slab部分滿和空連結串列都沒有了,則會呼叫get_freepage()從夥伴系統分配2的冪的頁面。

slab機制實際上就是kmalloc的底層實現。

如果核心需要分配不連續的記憶體,那麼會呼叫vmalloc,vmalloc建立核心虛擬記憶體對物理儲存器的頁面對映,但由於vmalloc向夥伴系統要要記憶體不連續,所以相對kmalloc效率會低一些。vmalloc分配時使用__GFP_HIGHMEM標誌,優先從高階記憶體獲取物理頁。

vmalloc是對核心虛擬記憶體的對映,和程式記憶體分配類似,因使用缺頁中斷的機制,下面會說到。

程式的記憶體分配

建立程式fork(),動態記憶體malloc()都涉及到程式的記憶體分配。作業系統對於程式的記憶體分配,以mmap為例。malloc對於大於128k的記憶體使用mmap分配,mmap底層呼叫do_mmap(),由於是匿名對映,傳入file指標為NULL,然後會選擇呼叫當前程式的current->mm->get_unmapped_ared,在程式的虛擬地址空間分配一個VMA區域,並將該新的VMA加入到mm_struct管理的VMA連結串列和紅黑樹中。此時,核心並不為程式分配實際的物理頁面(頁表項標記不在記憶體)。當使用者程式第一次訪問這片記憶體時,虛擬地址經過MMU轉換,通過頁目錄查詢到相應頁表項,頁表項中標誌表明對應頁不再記憶體中。則觸發缺頁中斷。由於是匿名對映,核心在記憶體中找到一塊犧牲頁,將犧牲頁換出記憶體,將記憶體中該頁的位置清零,並修改相應頁表項。然後重啟引發缺頁中斷的指令,這時相應頁面已經在記憶體中了,程式就可以正常使用記憶體了。

參考:

  1. linux核心—–記憶體管理相關技術
  2. linux記憶體管理總結之記憶體分配
  3. Linux記憶體管理機制 buddy夥伴演算法
  4. linux記憶體管理–缺頁異常處理
  5. linux缺頁異常剖析–使用者空間

相關文章