漫畫解說 “內(nèi)存映射”
本文轉(zhuǎn)載自微信公眾號「Linux內(nèi)核那些事」,作者songsong001。轉(zhuǎn)載本文請聯(lián)系Linux內(nèi)核那些事公眾號。
虛擬內(nèi)存空間與物理內(nèi)存空間
虛擬內(nèi)存地址就好比每個班的學(xué)號,而物理內(nèi)存地址就好比真實的學(xué)生。因為每個學(xué)號都對應(yīng)不同的學(xué)生,所以虛擬內(nèi)存地址也要映射到物理內(nèi)存地址。
虛擬內(nèi)存與物理內(nèi)存的映射關(guān)系是通過 頁表 來關(guān)聯(lián)的,如下圖:
但 頁表 并不是按字節(jié)來進(jìn)行映射的,而是按照 內(nèi)存頁 為單位進(jìn)行映射,一般一個 內(nèi)存頁 的大小為 4KB(為什么要加一般呢,這是因為除了4KB,還有其他大小的內(nèi)存頁,如2MB,4MB,1GB等),頁表 的每一個 頁表項 都保存著物理內(nèi)存頁的地址。
所以,4GB 的虛擬內(nèi)存空間需要 1MB 大小的頁表來關(guān)聯(lián)(因為 4GB / 4KB = 1MB)。也就是說,0 ~ 4095 的虛擬內(nèi)存地址都是使用 頁表 的第一個 頁表項 來映射的,而 4096 ~ 8191 的虛擬內(nèi)存地址使用 頁表 的第二個 頁表項 來映射的,以此類推...
那么,通過什么樣的算法能把 0 ~ 4095 的虛擬內(nèi)存地址轉(zhuǎn)換為頁表的第一個頁表項呢?其實很簡單,只需要把虛擬內(nèi)存地址的高端 20 位作為頁表的索引,而把低端 12 位作為內(nèi)存頁中的偏移量即可,如下圖:
在上圖中,還看到了一個 cr3 的東西,這是 CPU 中的一個寄存器,用于保存 頁表 的物理內(nèi)存地址,通過這個寄存器就能找到進(jìn)程的 頁表 了。
現(xiàn)在對內(nèi)存映射的原理有了比較清晰的了解了,但現(xiàn)在有個問題,每個進(jìn)程都要 1MB 大小的頁表,那不是很浪費(fèi)內(nèi)存嗎?的確是,因為進(jìn)程很多虛擬內(nèi)存地址并不會用到,為了節(jié)省頁表使用的內(nèi)存,x86 CPU 把頁表分為 2 級,如下圖:
如上圖所示,把原來的 頁表 劃分為 頁目錄 和 頁表,它們的大小均為 4KB。而虛擬內(nèi)存地址的高 10 位作為頁目錄的索引,而中間 10 位作為頁表的索引,低 12 位還是作為物理內(nèi)存頁的偏移量。
把原來的 頁表 劃分為兩級后,進(jìn)程有些不使用的虛擬內(nèi)存地址就不需要進(jìn)行映射,從而節(jié)省了內(nèi)存的使用。