例如 使用者態通過brk申請了一塊記憶體,後續訪問這塊記憶體的0x00007f88f16a4690
這塊位址會發生什麼?
首先,x64核心是4級頁表,根據x64對線性位址的劃分,可以計算出0x00007f88f16a4690
這位址的pgd索引是 255, pud索引是 35, pmd索引是 395, pte索引是 164。
因為x64體系結構,pgd索引為 47~39 bit,pud索引為 38~30 bit,pmd索引為 29~21 bit,pte索引為 20~12 bit。
首先,從current->mm->pgd
取出全域性頁表的首位址,current->mm->pgd
是pgd_t
型別資料結構,其實就是8位元組的unsigned long
位址,所以根據 pgd索引可以獲得pgd = current->mm->pgd + 255,
接著,取出的pgd中,即獲取*pgd中的值,也是乙個unsigned long的值。
*pgd為null的話,建立乙個page1,page1的頁框號,就儲存在 pgd中,而pud的值,就是該page的對應的虛擬位址加上358位元組,換句話說,*pgd具體存著pud的頁框號,通過該頁框號,能夠找到page,然後通過
記憶體偏移能夠找到當前位址對應的pud。
同理,*pud 為null的話,也會建立乙個page2,pud 儲存page2的頁框號,pmd就是page2的虛擬位址加上 3958。
最後,*pmd為null的話,建立乙個page3,pmd存入page3的頁框號,pte的位址就是page3的虛擬位址加上 1648。
用圖來描述,就是下面這樣子:
至此定址到了記憶體位址對應的pte,但是虛擬位址對應的實體地址還未建立,上面描述的page1,page2,page3都是為了構建多級頁表建立記憶體而已。mm->pgd
----
| 0 |
----
. .
. *pgd
---- pgd ----
| 255| -> | 0 |
---- ----
. .
. . *pud
. ---- pud ----
---- | 35 | -> | 0 |
| 511| ---- ----
---- . .
. . *pmd
---- ---- pmd ----
| 511| | 395| -> | 0 |
---- ---- ----
. .
. .
---- ---- pte(8位元組)
| 511| | 164| <-
---- ----..
----
| 511|
----
物理記憶體的建立是在函式do_anonymous_page
中完成。do_anonymous_page
函式中,對於讀操作,分配乙個zero page
,它是乙個固定的頁,啟動就分配好的頁,因為走到do_anonymous_page
這步時,必然是因為虛擬記憶體對應的屋裡記憶體還未建立,所以此時使用者態的read
型別的操作,返回0即可,其實返回任意資料即可。且將這個pte標記沒有設定成可寫。這樣意味著
後續的寫操作,會申請新的page,這個page才是虛擬位址對應的物理記憶體。(在do_wp_page
中處理)。
回到上面的圖,我們來看一下,乙個程序最壞情況下,乙個程序頁表最大會多大?看上圖,乙個pmd 4k,乙個pud有512個pmd,乙個pgd有512個pmd,總共512個pgd,
所以總共會消耗記憶體:4k512512*512 = 549755813888 bytes = 512g。還有一種更簡單的演算法,就是64bit有效位址範圍是48位元組,所以總共記憶體2^48,而又被4k大小的page分割,所以
會被劃分成 248/212 個 page,而乙個page需要8位元組的pte來表示所以(248/212)*2^3 = 2^39 = 549755813888 = 512g。
這個pte就是被會快取在tlb中,可見上面極端情況下,乙個程序會有 549755813888/8 個pte表,如果程序申請的記憶體夠大,訪問夠隨機,基本次次tlb miss。
上面描述的pagesize是4k,如果pagesize大一點會怎麼樣。我們舉個極端例子,記憶體是512gb,pagesize是256gb,顯然,乙個程序只需要2 pte就能構建虛擬位址和物理位址對映關係。
而pte的數量急劇減少,被tlb淘汰的概率幾乎沒有。但是劣勢也非常明顯,如果程序開闢1位元組的記憶體,就需要申請乙個256g的page。
函式呼叫時發生了什麼
我們下面就來 一下高階語言中函式的呼叫和遞迴等性質是怎樣通過系統棧巧妙實現的。請看如下 int func b int arg b1,int arg b2 int func a int arg a1,int arg a2 int main int argc,char argv,char envp 這段...
函式呼叫時發生了什麼
第一步 函式呼叫 1 對實參表從右向左,一次計算出實參的值,並且將值壓棧。2 將函式呼叫語句 儲存到在棧中,以便函式呼叫完成後返回。壓棧 3 跳轉到函式體處。第二步 函式體執行 4 如果函式體中定義了變數,將變數壓棧 5 將每乙個形參以棧中對應的 實參值取代,執行函式體的功能體。6 將函式體中的變數...
呼叫close 後發生了什麼
我們考慮簡單的客戶端 伺服器通訊的場景,其典型模式為 在tcp層面表現為 我們考慮一些異常情況,客戶端上 選用 unix網路程式設計 第一卷 中的伺服器 客戶端樣例程式。伺服器部分 int main int argc,char ar 伺服器監聽9999埠,新鏈結建立後,發出當前時間,隨後呼叫clos...