【底層原理】Linux內(nèi)存管理


點擊「閱讀原文」查看良許原創(chuàng)精品視頻。
作者:羅道文的私房菜
原文鏈接:http://luodw.cc/2016/02/17/linux-memory/
點擊「閱讀原文」查看良許原創(chuàng)精品視頻。
作者:羅道文的私房菜
原文鏈接:http://luodw.cc/2016/02/17/linux-memory/
今天這篇文章主要是之前看linux內(nèi)核相關(guān)知識和博客Gustavo Duarte中。我(指道文)主要是看了這篇博客,并且結(jié)合之前的知識,對內(nèi)存管理的的理解又上升了一個檔次。所以想通過這篇文章總結(jié)下。
我們先來看下linux內(nèi)存布局,此圖比我之前寫的那篇文章寫的布局更詳細

在linux中,每一個進程都被抽象為task_struct結(jié)構(gòu)體,稱為進程描述符,存儲著進程各方面的信息;例如打開的文件,信號以及內(nèi)存等等;然后task_struct中的一個屬性mm_struct管理著進程的所有虛擬內(nèi)存,稱為內(nèi)存描述符。在mm_struct結(jié)構(gòu)體中,存儲著進程各個內(nèi)存段的開始以及結(jié)尾,如上圖所示;這進程使用的物理內(nèi)存,即常駐內(nèi)存RSS頁數(shù),這個內(nèi)存使用的虛擬地址空間VSZ頁數(shù),還有這個進程虛擬內(nèi)存區(qū)域集合和頁表。
從上面這個圖可以看出,進程是有代碼段Text segment,數(shù)據(jù)段(已初始化的全局,靜態(tài)變量),BSS段(未初始化的全局,靜態(tài)變量),堆,內(nèi)存映射區(qū)以及棧;
每一塊虛擬內(nèi)存區(qū)(VMA)都是由一塊連續(xù)的虛擬地址組成,這些地址從不覆蓋。一個vm_area_struct實例描述了一塊內(nèi)存區(qū)域,包括這塊內(nèi)存區(qū)域的開始以及結(jié)尾地址;flags標志決定了這塊內(nèi)存的訪問權(quán)限和行為;vm_file決定這塊內(nèi)存是由哪個文件映射的,如果沒有文件映射,則這塊內(nèi)存為匿名的(anonymous)。上述圖中提到的每個內(nèi)存段,都對應(yīng)于一個vm_area_struct結(jié)構(gòu)。如下圖所示

上圖即為/bin/gonzo進程的內(nèi)存布局。程序的二進制文件映射到代碼段和數(shù)據(jù)段,代碼段為只讀只執(zhí)行,不可更改;全局以及靜態(tài)的未初始化的變量映射到BSS段,為匿名映射,堆和棧也是匿名映射,因為沒有相應(yīng)的文件映射;內(nèi)存映射區(qū)可以映射共享庫,映射文件以及匿名映射,所以這塊內(nèi)存段可以是文件映射也可以是匿名映射。而且不同的文件,映射到不同的vm_area_struct區(qū)。
這些vm_area_struct集合存儲在mm_struct中的一個單向鏈表和紅黑樹中;當輸出/proc/pid/maps文件時,只需要遍歷這個鏈表即可。紅黑樹主要是為了快速定位到某一個內(nèi)存塊,紅黑樹的根存儲在mm_rb域。
之前介紹過,線性地址需要通過頁表才能轉(zhuǎn)換為物理地址。每個進程的內(nèi)存描述符也保存了這個進程頁表指針pgd,每一塊虛擬內(nèi)存頁都和頁表的某一項對應(yīng)。
虛擬內(nèi)存是不存儲任何數(shù)據(jù)的,它只是將地址空間映射到物理內(nèi)存。物理內(nèi)存有內(nèi)核伙伴系統(tǒng)分配,如果一塊物理內(nèi)存沒有被映射,就可以被伙伴系統(tǒng)分配給虛擬內(nèi)存。剛分配的物理內(nèi)存葉框可能是匿名的,存儲進程數(shù)據(jù),也可能是也緩存,存儲文件或塊設(shè)備的數(shù)據(jù)。一塊虛擬內(nèi)存vm_area_struct塊是由連續(xù)的虛擬內(nèi)存頁組成的,而這些虛擬內(nèi)存塊映射的物理內(nèi)存卻不一定連續(xù),如下圖所示:

如上圖所示,有三個頁映射到物理內(nèi)存,還有兩個頁沒有映射,所以常駐內(nèi)存RSS為12kb,而虛擬內(nèi)存大小為20kb。對于有映射到物理內(nèi)存的三個頁的頁表項PTE的Present標志設(shè)為1,而兩個沒有映射物理內(nèi)存的虛擬內(nèi)存頁表項的Present位清除。所以這時訪問那兩塊內(nèi)存,則會導致異常缺頁。
vma就像應(yīng)用程序和內(nèi)核的一個契約。當應(yīng)用程序申請內(nèi)存或者文件映射時,內(nèi)核先響應(yīng)這個請求,分配或更新虛擬內(nèi)存;但是這些虛擬內(nèi)存并沒有映射到真實的物理內(nèi)存。而是等到內(nèi)存訪問產(chǎn)生一個內(nèi)存異常缺頁時才真正映射物理內(nèi)存。即當訪問沒有映射的虛擬內(nèi)存時,由于頁表項的Present位沒有被設(shè)置,所以此時會產(chǎn)生一個缺頁異常。vma記錄和頁表項兩個在解決內(nèi)存缺頁,釋放內(nèi)存以及內(nèi)存swap out都起著重要的作用。下面圖展示了上述情況:

1、一開始堆中只有8kb的內(nèi)存,而且都已經(jīng)映射到物理內(nèi)存;
2、當調(diào)用brk()函數(shù)擴展堆時,新的頁是沒有映射到物理內(nèi)存的,
3、當處理器需要訪問一個地址,而且這個地址在上述剛分配的虛擬內(nèi)存中,這時產(chǎn)生一個缺頁異常;
4、這時進程向伙伴系統(tǒng)申請一頁的物理內(nèi)存,映射到那塊虛擬內(nèi)存上,并添加頁表項,設(shè)置Present位.
自此,這個內(nèi)存管理暫時就說到這。總結(jié)下:
1、linux進程的內(nèi)存布局的每個段都是有一個vm_area_struct,而這個實例是由連續(xù)的虛擬內(nèi)存地址組成;
2、當請求內(nèi)存時,先是擴展vm_area_struct或者新分配一個vm_area_struct,但是并不映射物理內(nèi)存,只有等到訪問這塊內(nèi)存時,產(chǎn)生缺頁異常,內(nèi)核才分配物理內(nèi)存。
推薦閱讀:
Sampler:Shell命令執(zhí)行可視化和告警工具
5T技術(shù)資源大放送!包括但不限于:C/C++,Linux,Python,Java,PHP,人工智能,單片機,樹莓派,等等。在公眾號內(nèi)回復「1024」,即可免費獲取!!
