1. 程式人生 > 其它 >【底層原理】Linux記憶體管理

【底層原理】Linux記憶體管理

作者:羅道文的私房菜

原文連結:http://luodw.cc/2016/02/17/linux-memory/

今天這篇文章主要是之前看linux核心相關知識和部落格Gustavo Duarte中。我(指道文)主要是看了這篇部落格,並且結合之前的知識,對記憶體管理的的理解又上升了一個檔次。所以想通過這篇文章總結下。

我們先來看下linux記憶體佈局,此圖比我之前寫的那篇文章寫的佈局更詳細

在linux中,每一個程序都被抽象為task_struct結構體,稱為程序描述符,儲存著程序各方面的資訊;例如開啟的檔案,訊號以及記憶體等等;然後task_struct中的一個屬性mm_struct管理著程序的所有虛擬記憶體,稱為記憶體描述符。在mm_struct結構體中,儲存著程序各個記憶體段的開始以及結尾,如上圖所示;這程序使用的實體記憶體,即常駐記憶體RSS頁數,這個記憶體使用的虛擬地址空間VSZ頁數,還有這個程序虛擬記憶體區域集合和頁表。

從上面這個圖可以看出,程序是有程式碼段Text segment,資料段(已初始化的全域性,靜態變數),BSS段(未初始化的全域性,靜態變數),堆,記憶體對映區以及棧;

每一塊虛擬記憶體區(VMA)都是由一塊連續的虛擬地址組成,這些地址從不覆蓋。一個vm_area_struct例項描述了一塊記憶體區域,包括這塊記憶體區域的開始以及結尾地址;flags標誌決定了這塊記憶體的訪問許可權和行為;vm_file決定這塊記憶體是由哪個檔案對映的,如果沒有檔案對映,則這塊記憶體為匿名的(anonymous)。上述圖中提到的每個記憶體段,都對應於一個vm_area_struct結構。如下圖所示

上圖即為/bin/gonzo程序的記憶體佈局。程式的二進位制檔案對映到程式碼段和資料段,程式碼段為只讀只執行,不可更改;

遊戲出售地圖全域性以及靜態的未初始化的變數對映到BSS段,為匿名對映,堆和棧也是匿名對映,因為沒有相應的檔案對映;記憶體對映區可以對映共享庫,對映檔案以及匿名對映,所以這塊記憶體段可以是檔案對映也可以是匿名對映。而且不同的檔案,對映到不同的vm_area_struct區。

這些vm_area_struct集合儲存在mm_struct中的一個單向連結串列和紅黑樹中;當輸出/proc/pid/maps檔案時,只需要遍歷這個連結串列即可。紅黑樹主要是為了快速定位到某一個記憶體塊,紅黑樹的根儲存在mm_rb域。

之前介紹過,線性地址需要通過頁表才能轉換為實體地址。每個程序的記憶體描述符也儲存了這個程序頁表指標pgd,每一塊虛擬記憶體頁都和頁表的某一項對應。

虛擬記憶體是不儲存任何資料的,它只是將地址空間對映到實體記憶體。實體記憶體有核心夥伴系統分配,如果一塊實體記憶體沒有被對映,就可以被夥伴系統分配給虛擬記憶體。剛分配的實體記憶體葉框可能是匿名的,儲存程序資料,也可能是也快取,儲存檔案或塊裝置的資料。一塊虛擬記憶體vm_area_struct塊是由連續的虛擬記憶體頁組成的,而這些虛擬記憶體塊對映的實體記憶體卻不一定連續,如下圖所示:

如上圖所示,有三個頁對映到實體記憶體,還有兩個頁沒有對映,所以常駐記憶體RSS為12kb,而虛擬記憶體大小為20kb。對於有對映到實體記憶體的三個頁的頁表項PTE的Present標誌設為1,而兩個沒有對映實體記憶體的虛擬記憶體頁表項的Present位清除。所以這時訪問那兩塊記憶體,則會導致異常缺頁。

vma就像應用程式和核心的一個契約。當應用程式申請記憶體或者檔案對映時,核心先響應這個請求,分配或更新虛擬記憶體;但是這些虛擬記憶體並沒有對映到真實的實體記憶體。而是等到記憶體訪問產生一個記憶體異常缺頁時才真正對映實體記憶體。即當訪問沒有對映的虛擬記憶體時,由於頁表項的Present位沒有被設定,所以此時會產生一個缺頁異常。vma記錄和頁表項兩個在解決記憶體缺頁,釋放記憶體以及記憶體swap out都起著重要的作用。下面圖展示了上述情況:

1、一開始堆中只有8kb的記憶體,而且都已經對映到實體記憶體;

2、當呼叫brk()函式擴充套件堆時,新的頁是沒有對映到實體記憶體的,

3、當處理器需要訪問一個地址,而且這個地址在上述剛分配的虛擬記憶體中,這時產生一個缺頁異常;

4、這時程序向夥伴系統申請一頁的實體記憶體,對映到那塊虛擬記憶體上,並新增頁表項,設定Present位.

自此,這個記憶體管理暫時就說到這。總結下:

1、linux程序的記憶體佈局的每個段都是有一個vm_area_struct,而這個例項是由連續的虛擬記憶體地址組成;

2、當請求記憶體時,先是擴充套件vm_area_struct或者新分配一個vm_area_struct,但是並不對映實體記憶體,只有等到訪問這塊記憶體時,產生缺頁異常,核心才分配實體記憶體。