Linux中內核頁表是幹嘛用的,為什麼要有內核頁表?

在本科的時候學了操作系統,裡面說頁表的作用是從線性地址到物理地址的映射。每一個進程都有自己的頁表,也就是自己的地址空間,當程序運行的時候,它就可以根據自己的地址找到自己程序所在的位置,然後進行執行,我想問的是內核頁表也是同樣的嗎?我感覺知識遷移不過來啊,,,有點迷惑,謝謝大家。


嚴格的說,32-bit x86 分為頁目錄和頁表。64-bit 的級別更多,還有不同模式。下面的答案就把這些 MMU 使用的數據統稱為「頁表」。

內核空間也是同樣通過「頁表」訪問的,因為現代 CPU 的定址不能繞過 MMU。不過內核空間和用戶空間不同,它一般不做 swap,也就沒有 page fault,而且它一般不會把連續的虛擬地址空間映射成不連續的物理空間,一般只是做一個 offset。所以所有進程的用於訪問內核空間的頁表都是這麼設定的。


在32位的系統,內核頁表為了記錄 內核 對高端內存訪問而進行的映射關係


在古老的操作系統裡面,所有進程都是共用同一物理內存空間的,這種方法會有一些問題,比如兩個進程之前相互踩內存,一個進程污染(踩內存)後,無法隔離,必須整個系統複位,才能恢復乾淨的環境。在這種操作系統下,進程之間無法隔離。

為了解決進程之間內存隔離,提供了虛擬內存這個概念。

進程看到的是虛擬內存,這根本看不到物理內存,物理內存是OS給它分配的,它不需要感知物理內存。對於同一程序運行起來的兩個進程,它們的虛擬空間布局可能完全一樣,但他們真實使用的物理內存空間則不相同,通過這種方式來實現進程之間的隔離。

就像廣州人民和上海人民都說中山路一樣,說法一樣,但所指的地址是不一樣的(地點的經緯度不一樣)。

那麼操作系統如何利用虛擬內存實現這種隔離呢?這需要處理器提供虛擬內存功能,在X86處理器,支持保護模式的處理都提供虛擬內存功能,ARM處理器則叫MMU功能。

進程訪問虛擬內存,CPU執行時通過分頁機制轉換成物理內存訪問,虛擬地址到物理內存的轉換表稱為頁表,這個轉換是個查表的過程。

同一程序運行起來的兩個進程,虛擬地址空間相同,但對應的物理空間是不相同的。OS需要給每個進程設置一份頁表,在進程調度過程中,上下文切換階段會做頁表的切換。

每個進程的頁表都是由OS來管理的,你所說的內核頁表,就是指內核管理的,每個進程都有唯一一份的頁表。

每進程可以訪問的地址空間分為用戶態和內核態,在Linux操作系統裡面,所有進程在內核態空間里,va &<-&> pa的對應空間是相同的。所以每個進程的頁表管理的空間包含用戶態和內核態空間,所有進程內核態空間到物理地址空間是相同的。


詳細內容請參考《深入理解LINUX內核》一書,裡面有非常精彩的講解。

1、寫在指令中的地址統統都是虛擬地址,而不是實際的物理地址!

2、在X86這種同時進行段頁式管理的硬體上,虛擬地址分成兩級。指令中的那個值被稱為「邏輯地址」,送給MMU中的「分段單元」,轉換成線性地址。

3、線性地址再通過「分頁單元」,轉換成物理地址。

4、這個轉換關係是需要預先配置好的,「分段單元」和「分頁單元」都要有自己的硬體配置。

5、對「分頁單元」來說,頁表就是硬體配置,這個是繞不過去的。


先說明一下基本知識:首先,Linux的每個進程都有單獨的頁表,其中內核線程使用的是內核的頁表。我們先不討論內核線程這種情況,對於普通的進程來說,都有一個叫做mm_struct的結構體,它的成員pgd會指向內存中這個進程對應的頁表。這個頁表中的每一項(當然並不是所有頁表項都是有效的)會描述整個虛擬內存空間。Linux userspace只能訪問低於0xC0000000也就是PAGE_OFFSET的內存,高於它的內存屬於內核空間地址。

2. 對於普通進程來說,內核頁表只是進程頁表的一部分,它不是單獨的一個東西。我們知道,Linux內核是為了服務於用戶態的。正在運行的程序陷入內核態基本手段包括中斷,系統調用。當CPU進入內核態之後,CPU訪問內存地址還是要通過虛擬地址來訪問的。此時訪問虛擬地址就是通過被打斷的進程的頁表項才找到對應物理地址的。

3. 內核頁表其實在剛開機的時候就已經初始化好了,它存放的地址是swapper_pg_dir,也就是0XC0004000.這個地址里的每一項會描述1M的內存,0xc0007000到0xc0008000這段描述內核態的地址。每個用戶態進程創建的時候,內核都會將這個頁表複製到進程的頁表中。而對於內核線程來說,由於一直工作在內核態,使用這個內核頁表也就足夠了。


好問題,先佔坑,等忙完這一段時間,好好回答一下。

先說結論,內核空間也是需要頁表的。

@張光墨的答案是正確的。

以32位x86機器為例,先簡單解釋一下。一旦CPU進入了保護模式,開啟分頁機制,不管在內核空間,還是用戶空間,都是使用虛擬地址進行定址了。這一點才是內核必須使用頁表的根本原因。

比如在head.S中,寫call _start_kernel,經過編譯器和鏈接器編譯過後,其實變成了這樣:

call 0xC0123456

這個地址是我隨便寫的。但一定是大於0xC0000000的。

這個地址就是虛擬地址,而不是物理地址。當CPU執行這個call指令的時候,要把這個虛擬地址通過MMU來轉換成物理地址。

這是簡單的回答。中間還有很多問題,比如內核頁表是怎麼準備好的,這個0xC0000000是在哪裡指定的,鏈接器是怎麼知道把地址加上這個神奇的數字等等問題,等我以後再更吧。


內核不需要頁表。

1. 所謂的頁表是進程從硬碟載入到內存時,用來記錄進程在內存位置的表格。擁有頁表的前提是什麼呢?一個進程要從硬碟載入到內存,要申請內存空間。

2. 所謂的內核是「賴在」內存不走的一組程序,從開機時就被載入到內存,直到關機才被從內存趕走。

3. 如果把計算機比喻成宇宙,那麼內核就是上帝。上帝和宇宙同時產生,因此上帝先把自己想占的地方先佔據了。如果是別人要佔用某個地方,需要給上帝提交申請表,這個申請表就是頁表。


推薦閱讀:

linux 怎麼限制使用命令?
Linux交換空間可以和其他目錄放在不同的硬碟中嗎?
大家都用linux做什麼?我剛安裝debian9,如何用它學習編程?

TAG:Linux系統管理 | 內核編譯 | Linux內核 |