Linux內存布局


我們先來看下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,但是並不映射物理內存,只有等到訪問這塊內存時,產生缺頁異常,內核才分配物理內存。

本文地址:https://www.linuxprobe.com/linux-memory-layout.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM