Linux頁表中虛擬內存地址如何映射到硬碟數據塊地址?

當頁表發現虛擬內存的內容不在物理內存中會發生缺頁,這時候,需要把硬碟上某個數據塊讀入內存,並修改頁表。問題是,內核怎麼指定需要去讀取磁碟上的哪一個數據塊呢?

虛擬內存如果映射的是物理內存,那麼頁表的內容是物理內存地址,那麼如果映射的是磁碟數據塊,那麼頁表的內容是什麼呢?


謝邀 @逸文奧特曼

Linux頁表中虛擬內存地址如何映射到硬碟數據塊地址? 顯然這個問題是不對的,CPU沒有直接訪問磁碟的能力,CPU頁表裡填的永遠是內存地址,除非這個磁碟是掛到地址匯流排上,而且可以像普通內存一樣訪問。

Linux進程地址空間可以分成兩大類,文件映射和匿名映射。文件映射,顧名思義,是指該地址空間的內容來自於一個文件;而匿名映射地址空間背後什麼靠山都沒有。進程的代碼段來自於鏡像,採用文件映射方式;而棧,堆,bss段,數據段均是匿名映射。

那問題來了,CPU執行代碼時,是怎麼把磁碟上的內容載入到內存並執行呢?

因為Linux永遠採用延遲的分配策略,總在不到最後,都不會將文件載入到內存中,所以經常將初學者搞糊混,如果不懂Linux這個機制,反倒很容易明白。所以,我們是不是鑽進一個細節裡面,反倒是把OS的承諾給忘了(OS內部的延遲機制對用戶態進程永遠是透明的,不需要感知)。

下面以代碼段為例,講講文件映射是怎麼實現的。

首先:Linux上可執行文件,會描述代碼段的虛擬地址空間(start和size),同樣也會描述該段內存在文件的位置(offset和size)。

然後:Linux載入進程時(exec系列系統調用)會為該地址空間分配一個 VMA,vma數據結構會描述虛擬空間的開始地址,以及空間大小,同時會描述該vma背後映射的文件名(或路徑)、映射空間所在文件的偏移量和大小。但是Linux內核不給該空間分配物理內存,所以此時的頁表項是空的。當vma設置好之後,就算載入完事了,跳到"main"函數開始執行。

再後:當一旦執該代碼段時,由於頁表項不存在,CPU會產生一次違例訪存,跳進OS早早就調置好的缺頁異常代碼(do_page_fault),在do_page_fault函數內經過嚴密的安檢之後,確認程序訪存的合法性之後,就要將文件載入到內存中。

接著:do_page_fault根據vma裡面描述的文件名,調用文系統介面將文件內存載入到物理內存中。等等……這個過程也涉及很複雜的載入過程,涉及磁碟訪問,bio,以及pagecache,可以在知乎上找很多相關的描述。

最後:文件載入到物理內存(就是pagecache)後,可以修改頁表項了,將頁有的PFN填寫成物理內存的頁框號,訪問屬性等一系的頁表位寫之後,就算完了,返回用戶態。繼續執行。


以下基於linux內核2.4版本,如有錯誤,歡迎指出

1.可以通過file_structs-&>file-&>dentry-&>inode獲取其inode,然後inode作為參數通過ext2_get_block()函數獲得文件的邏輯塊iblock,然後通過inode中的i_data找到記錄塊落在哪個區間(ext2_blocks_to_path)由此確定其磁碟位置

2.題主應該說的是文件映射到虛擬磁碟,頁表映射到哪裡吧。

先下結論:文件映射時,實際頁表映射的是page cache.(擴充:另外共享內存之所以幾個進程可以映射到同一片內存區,實際從tmpfs內存文件系統創建打開文件,映射到同一個文件,也是由於page cache).文件映射到內存,實際只是增加vm_area_structs,並沒有一開始就映射到內存。等到r/w時才由缺頁異常處理,發現不在page cache或者swap cache,就從磁碟載入到內存,並且將頁插入到page cache中,page-&>mapping-&>address_space結構就是管理page cache的,而每個file都有一個address_space結構管理緩衝頁。file-&>dentry-&>inode-&>i_mappin

(圖片來自:Linux內核源代碼情景分析)


Swap Management

11.2


其實Linux的實現很簡單,對於每個進程來說,每段內存段,比如映射的文件的內存段,都用一個VMA來描述,VMA結構體中,有一個file指針指向它的backend硬碟文件。當user space進程訪問此地址產生缺頁中斷後,會陷入內核態將硬碟文件里的內容準備好,程序就可以繼續執行了。不知道有沒有回答好你的問題?


第一問:

缺頁中斷分兩種情況:

1、只有當缺頁發生在進程的代碼段和數據段時,才會從磁碟中讀4KB數據載入到內存(一個物理頁);

2、若發生在進程地址空間的其他地方(堆、棧空間),則直接申請一個空閑物理頁,將該物理頁與發生中斷的線性頁建立映射關係。

因此,題主應該指的是第一種情況。進程代碼段和數據段在磁碟中保存在該進程的二進位可執行文件中,而該文件的位置由進程描述符中有一個欄位間接的指向。所以,當發生缺頁中斷後,OS通過該欄位找到進程相應的二進位可執行文件,然後進行載入操作。

第二問:

1、首先,「頁表的內容是物理內存地址」描述不太精確,但是核心意思沒有問題。所以頁表中頁表項的內容不可能和「磁碟數據塊」有任何直接關係。

2、題主的問題中,涉及到3個對象,虛擬內存(線性頁)、物理內存(物理頁、內存緩衝塊)和磁碟數據塊。這三者可以有的映射關係有兩個,線性頁--&>物理頁;內存緩衝塊--&>磁碟數據塊。前者的映射關係保存在頁表的頁表項中;後者的保存在OS的專門的數據結構中。

(回答來源linux0.11,不一定適用於最新內核)


我來回答一下,不確定對不對。首先每個進程都有自己的名字空間,你說當發現需要的數據不在物理內存中的時候需要去磁碟讀取數據到內存中!這個問題是首先進程是知道自己需要什麼數據的,它才知道這些數據在不在物理內存中!這些數據是什麼呢?比如說文件的元數據這是很重要的,經常都是緩存在內存中!額如果說元數據你不知道是什麼的話,那這麼說吧。比如說文件的數據你第一次讀取的時候有部分是緩存在內存中的,以免你過一會就又重新讀取這個文件(當然存在內存的時間看各種操作系統自己的機制啦,比如有的10ms就將數據刷回硬碟)。。繼續,然後,你再次讀取這個文件,然後內核發現該文件數據有部分存儲在內存中,可是有的不在!重點來了,意思是內核知道不在內存中的這部分數據是磁碟的哪部分數據的,因此就會去磁碟對應位置讀取這部分不在內存中的數據。

額,如果你非要問內核怎麼知道這部分不在內存中的數據在磁碟的什麼位置的話。。這。。說起來就很複雜啦。。每個文件在內存中有自己的一個address space 維護還文件的一顆radix-tree 這棵樹就描述著這個文件的磁碟數據在磁碟的位置與物理內存page的對應關係(這樣說也不是很對。。可以這麼理解)。。內核發現要讀取的這個文件的某個位置的數據對應的page不在這棵樹上(這樣說也不是很對,你就這麼理解吧)。。然後就會提交一個申請塊的bio 去磁碟申請數據去啦。。哎呀打完了。不對的地方求指教!


推薦閱讀:

mount --bind 可以算是ln一個目錄么?
長期使用一種linux發行版沒有重裝過是一種什麼樣的體驗?
進程地址空間

TAG:操作系統 | Linux | Linux內核 |