物理地址和虛擬地址的分布
來自專欄 人人都是極客
上一節內容的學習我們知道了CPU是如何訪問內存的,CPU拿到內存後就可以向其它人(kernel的其它模塊、內核線程、用戶空間進程、等等)提供服務,主要包括:
- 以虛擬地址(VA)的形式,為應用程序提供遠大於物理內存的虛擬地址空間(Virtual Address Space)
- 每個進程都有獨立的虛擬地址空間,不會相互影響,進而可提供非常好的內存保護(memory protection)
- 提供內存映射(Memory Mapping)機制,以便把物理內存、I/O空間、Kernel Image、文件等對象映射到相應進程的地址空間中,方便進程的訪問
- 提供公平、高效的物理內存分配(Physical Memory Allocation)演算法
- 提供進程間內存共享的方法(以虛擬內存的形式),也稱作Shared Virtual Memory
在提供這些服務之前需要對內存進行合理的劃分和管理,下面讓我們看下是如何劃分的。
物理地址空間布局
Linux系統在初始化時,會根據實際的物理內存的大小,為每個物理頁面創建一個page對象,所有的page對象構成一個mem_map數組。進一步,針對不同的用途,Linux內核將所有的物理頁面劃分到3類內存管理區中,如圖,分別為ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。
- ZONE_DMA 的範圍是 0~16M,該區域的物理頁面專門供 I/O 設備的 DMA 使用。之所以需要單獨管理 DMA 的物理頁面,是因為 DMA 使用物理地址訪問內存,不經過 MMU,並且需要連續的緩衝區,所以為了能夠提供物理上連續的緩衝區,必須從物理地址空間專門劃分一段區域用於 DMA。
- ZONE_NORMAL 的範圍是 16M~896M,該區域的物理頁面是內核能夠直接使用的。
- ZONE_HIGHMEM 的範圍是 896M~結束,該區域即為高端內存,內核不能直接使用。
Linux內核空間虛擬地址分布
在 Kernel Image 下面有 16M 的內核空間用於 DMA 操作。位於內核空間高端的 128M 地址主要由3部分組成,分別為 vmalloc area、持久化內核映射區、臨時內核映射區。
由於 ZONE_NORMAL 和內核線性空間存在直接映射關係,所以內核會將頻繁使用的數據如 Kernel 代碼、GDT、IDT、PGD、mem_map 數組等放在 ZONE_NORMAL 里。而將用戶數據、頁表(PT)等不常用數據放在 ZONE_HIGHMEM 里,只在要訪問這些數據時才建立映射關係(kmap())。比如,當內核要訪問 I/O 設備存儲空間時,就使用 ioremap() 將位於物理地址高端的 mmio 區內存映射到內核空間的 vmalloc area 中,在使用完之後便斷開映射關係。
Linux用戶空間虛擬地址分布
用戶進程的代碼區一般從虛擬地址空間的 0x08048000 開始,這是為了便於檢查空指針。代碼區之上便是數據區,未初始化數據區,堆區,棧區,以及參數、全局環境變數。
Linux物理地址和虛擬地址的關係
Linux 將 4G 的線性地址空間分為2部分,0~3G 為 user space,3G~4G 為 kernel space。
由於開啟了分頁機制,內核想要訪問物理地址空間的話,必須先建立映射關係,然後通過虛擬地址來訪問。為了能夠訪問所有的物理地址空間,就要將全部物理地址空間映射到 1G 的內核線性空間中,這顯然不可能。於是,內核將 0~896M 的物理地址空間一對一映射到自己的線性地址空間中,這樣它便可以隨時訪問 ZONE_DMA 和 ZONE_NORMAL 里的物理頁面;此時內核剩下的 128M 線性地址空間不足以完全映射所有的 ZONE_HIGHMEM,Linux 採取了動態映射的方法,即按需的將 ZONE_HIGHMEM 里的物理頁面映射到 kernel space 的最後 128M 線性地址空間里,使用完之後釋放映射關係,以供其它物理頁面映射。雖然這樣存在效率的問題,但是內核畢竟可以正常的訪問所有的物理地址空間了。
到這裡我們應該知道了 Linux 是如何用虛擬地址來映射物理地址的,最後我們用一張圖來總結一下:
推薦閱讀:
※一個新手面試 Linux 運維工作至少需要知道哪些知識?
※WSL 使用指南——07 通過腳本提高效率
※電腦複製粘貼背後發生了什麼?
※Android Studio如何調試Framework層的代碼?
※為什麼那麼多「國產」操作系統?
TAG:Linux |