一步一圖帶你深入理解 Linux 虛擬內存管理( 五 )


在內核中使用 start_stack 標識棧的起始位置,RSP 寄存器中保存棧頂指針 stack pointer,RBP 寄存器中保存的是?;刂?。
在棧空間的下邊也有一段待分配區域用于擴展??臻g,在??臻g的上邊就是內核空間了 , 進程雖然可以看到這段內核空間地址,但是就是不能訪問 。這就好比我們在飯店里雖然可以看到廚房在哪里,但是廚房門上寫著 “廚房重地,閑人免進”,我們就是進不去 。

一步一圖帶你深入理解 Linux 虛擬內存管理

文章插圖
4.2 64 位機器上進程虛擬內存空間分布上小節中介紹的 32 位虛擬內存空間布局和本小節即將要介紹的 64 位虛擬內存空間布局都可以通過 cat /proc/pid/maps 或者 pmap pid 來查看某個進程的實際虛擬內存布局 。
我們知道在 32 位機器上,指針的尋址范圍為 2^32,所能表達的虛擬內存空間為 4 GB 。
那么我們理所應當的會認為在 64 位機器上,指針的尋址范圍為 2^64,所能表達的虛擬內存空間為 16 EB。虛擬內存地址范圍為:0x0000 0000 0000 0000 0000 - 0xFFFF FFFF FFFF FFFF。
好家伙 !!! 16 EB 的內存空間 , 筆者都沒見過這么大的磁盤,在現實情況中根本不會用到這么大范圍的內存空間,
事實上在目前的 64 位系統下只使用了 48 位來描述虛擬內存空間,尋址范圍為2^48 ,所能表達的虛擬內存空間為 256TB 。
其中低 128 T 表示用戶態虛擬內存空間 , 虛擬內存地址范圍為:0x0000 0000 0000 0000- 0x0000 7FFF FFFF F000。
高 128 T 表示內核態虛擬內存空間,虛擬內存地址范圍為:0xFFFF 8000 0000 0000- 0xFFFF FFFF FFFF FFFF。
這樣一來就在用戶態虛擬內存空間與內核態虛擬內存空間之間形成了一段 0x0000 7FFF FFFF F000-0xFFFF 8000 0000 0000的地址空洞,我們把這個空洞叫做 canonical address 空洞 。
一步一圖帶你深入理解 Linux 虛擬內存管理

文章插圖
那么這個 canonical address 空洞是如何形成的呢?
我們都知道在 64 位機器上的指針尋址范圍為 2^64,但是在實際使用中我們只使用了其中的低 48 位來表示虛擬內存地址,那么這多出的高 16 位就形成了這個地址空洞 。
大家注意到在低 128T 的用戶態地址空間:0x0000 0000 0000 0000 - 0x0000 7FFF FFFF F000 范圍中,所以虛擬內存地址的高 16 位全部為 0。
如果一個虛擬內存地址的高 16 位全部為 0 ,那么我們就可以直接判斷出這是一個用戶空間的虛擬內存地址 。
同樣的道理,在高 128T 的內核態虛擬內存空間:0xFFFF 8000 0000 0000 - 0xFFFF FFFF FFFF FFFF 范圍中 , 所以虛擬內存地址的高 16 位全部為 1。
也就是說內核態的虛擬內存地址的高 16 位全部為 1 ,如果一個試圖訪問內核的虛擬地址的高 16 位不全為 1 ,則可以快速判斷這個訪問是非法的 。
這個高 16 位的空閑地址被稱為 canonical。如果虛擬內存地址中的高 16 位全部為 0 (表示用戶空間虛擬內存地址)或者全部為 1 (表示內核空間虛擬內存地址),這種地址的形式我們叫做 canonical form,對應的地址我們稱作 canonical address。
那么處于 canonical address 空洞 :0x0000 7FFF FFFF F000 - 0xFFFF 8000 0000 0000 范圍內的地址的高 16 位 不全為 0 也不全為 1。如果某個虛擬地址落在這段 canonical address 空洞區域中,那就是既不在用戶空間 , 也不在內核空間,肯定是非法訪問了 。
未來我們也可以利用這塊 canonical address 空洞,來擴展虛擬內存地址的范圍 , 比如擴展到 56 位 。
在我們理解了 canonical address 這個概念之后,我們再來看下 64 位 Linux 系統下的真實虛擬內存空間布局情況:
一步一圖帶你深入理解 Linux 虛擬內存管理

文章插圖
從上圖中我們可以看出 64 位系統中的虛擬內存布局和 32 位系統中的虛擬內存布局大體上是差不多的 。主要不同的地方有三點:
  1. 就是前邊提到的由高 16 位空閑地址造成的canonical address 空洞 。在這段范圍內的虛擬內存地址是不合法的 , 因為它的高 16 位既不全為 0 也不全為 1,不是一個 canonical address,所以稱之為 canonical address 空洞 。
  2. 在代碼段跟數據段的中間還有一段不可以讀寫的保護段,它的作用是防止程序在讀寫數據段的時候越界訪問到代碼段 , 這個保護段可以讓越界訪問行為直接崩潰,防止它繼續往下運行 。
  3. 用戶態虛擬內存空間與內核態虛擬內存空間分別占用 128T , 其中低128T 分配給用戶態虛擬內存空間,高 128T 分配給內核態虛擬內存空間 。

    推薦閱讀