MIT 6.828 | JOS | 關於虛擬空間和物理空間的總結
🚩 Question:
做lab過程中越來越迷糊,明明虛擬空間和物理空間都是4G,為什么還要有映射?又為啥映射要像JOS這樣映射?
解決途徑:
停下來,根據當前lab的進展,再回頭看上學期操作系統的ppt & 上網沖浪查資料!意識到自己對於分段機制
理解不夠。
最強總結:https://www.cnblogs.com/tolimit/p/4775945.html?utm_source=tuicool&utm_medium=referral
解決過程:
分段機制主要功能只有兩點:
- 將物理內存划分為多個段,讓操作系統可以使用大於其地址線對應的物理內存(比如正常情況下32位地址線可以訪問4G大小的內存,但是有分段后則可訪問大於4G的內存)。
- 權限控制,將每個段設置權限位,讓不同的程序訪問不同的段。
🎈 linux內核只用到了權限控制的功能,但是我一直將其理解為第一個功能!!!
🚩🚩🚩🚩注意:JOS中的分段功能並沒有實現
正常的操作系統通常采用兩個部件來完成對內核地址的保護,一個是通過段機制來實現的,但是JOS中的分段功能並沒有實現。二就是通過分頁機制來實現,通過把頁表項中的 Supervisor/User位置0,那么用戶態的代碼就不能訪問內存中的這個頁。
物理地址空間
+------------------+ <- 0xFFFFFFFF (4GB)
| 32-bit |
| memory mapped |
| devices |
| |
/\/\/\/\/\/\/\/\/\/\
/\/\/\/\/\/\/\/\/\/\
| |
| Unused |
| |
+------------------+ <- depends on amount of RAM
| |
| |
| Extended Memory |
| |
| |
+------------------+ <- 0x00100000 (1MB)
| BIOS ROM |
+------------------+ <- 0x000F0000 (960KB)
| 16-bit devices, |
| expansion ROMs |
+------------------+ <- 0x000C0000 (768KB)
| VGA Display |
+------------------+ <- 0x000A0000 (640KB)
| |
| Low Memory |
| |
+------------------+ <- 0x00000000
GDT/LDT && 分段機制
采用的方式就是引入了數據結構GDT,即全局描述符表,不知道有多少人聽過?說簡單點,GDT就是一個數組,每一個元素就是一個描述符,多個組合一起就構成了全局描述符表。而每一個描述符共64位,包含了以下的這些信息:段基址、段長度、屬性。原來的段寄存器,比如CS,DS等存的值則不是段偏移了,而是GDT的索引,通過該索引就可以找到對應的描述符。現在是不是明白了呢?接下來我們再詳細的解釋一下一個描述符中各個位的意義吧。
LDT 可以看作是多級索引的第二級GDT。


Linux 4種段寄存器取值
也就是linux只有4種段的選擇。



保護模式地址映射

從實模式切換到保護模式
1、准備GDT
2、用lgdt指令加載gdtr
3、打開A20地址線
4、將cr0寄存器的最后一位(PE位)置1:
PE位為0時,CPU運行在實模式下;
PE位為1時,CPU運行在保護模式下.
5、跳轉,進入保護模式

bootloader為止的模型
🚩 感謝圖源
物理內存的分布
-
Lab1進入內存后,開啟分頁模式,將虛擬地址[0, 4MB)映射到物理地址[0, 4MB),[0xF0000000, 0xF0000000+4MB)映射到[0, 4MB)(/kern/entry.S)
-
lab1 結束后物理分布如下:

- lab2 中mem_init()結束之后,物理地址分布如下:

終極問題:物理空間和虛擬空間都是4G,為什么分布不一樣?
這個問題困擾了我很久,並且在迷迷糊糊做lab的時候沒有搞明白。
在總結梳理了前面的知識之后,終於可以解決這個問題了!
參考:
MIT 6.828 Lab Guide
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 線性地址空間里,使用完之后釋放映射關系,以供其它物理頁面映射。雖然這樣存在效率的問題,但是內核畢竟可以正常的訪問所有的物理地址空間了。
總結如下:
總結JOS 的虛擬空間和物理空間對應關系
做完實驗后,我將與虛擬空間和物理空間有關的內容進行了梳理,畫出了下圖:
1、[UPAGES, UVPT]: 在虛擬地址中的這段內存就是對應的在實際物理地址里 pages 數組的存儲位置。可以看到這段地址在ULIM之下,也就是說操作系統開放pages數組便於讓用戶程序可以訪問。為什么呢?比如說假如有的程序想要知道一個物理頁面被引用了多少次,那么根據相應的pages[i].pp_ref就可以知道了。我們看到這個區域在布局中分配了 PTSIZE 的大小,那么這個大小夠么?因為我們知道在物理頁面中pages所占用的大小為npage × sizeof (struct PageInfo) =npage ×8B(使用sizeof()查看,一個指針占4字節,uint16_t是2個字節,可是struct PageInfo占8字節,有些迷惑,可借鑒uint16_t類型在 Bochs 模擬出來的 x86 平台上是4字節理解)。我們看看PTSIZE的空間可以裝struct Page的個數為1024 ×4KB / 8B = 4MB / 8B = 1/2M。一個Page結構對應一個實際大小為4KB的物理頁面,所以這個PTSIZE大小的虛擬地址空間能夠管理 1/2 M × 4KB =2GB的物理內存,這個對於我們的QEMU模擬器來講應該還是足夠了。
2、[UVPT, ULIM]: 這部分是一個系統頁目錄——kern_pgdir。開放給用戶只讀,可以使用戶得到當前內存中某個虛擬地址對應的物物理頁面地址是多少。關於這部分,我們需要看一下:
kern/pmap.c
kern_pgdir這部分實際緊接在kernel 的 end 后面,也即在實際的物理地址里,上圖最下面一句話使以物理地址 PADDR(kern_pgdir) 開始的一頁成為了虛擬地址的 PDX = PDX(UVPT) 的頁表。這部分實際占物理空間 PGSIZE, 但是在虛擬地址上卻分配了 PTSIZE 大小。
注意 UVPD 的虛擬地址為 PDX(UVPT)<<22 | PDX(UVPT)<<12 | 0000000000 。
詳細的 UVPT 介紹見:Lecture 5
3、[KERNBASE, 4G] : 此部分映射實際物理內存中從0開始的中斷向量表IDT、BIOS程序、IO端口以及操作系統內核等,該部分虛擬地址 - KERNBASE 就是物理地址。
從這個布局結構我們就可以看出來,實際上虛擬內存中分配給用戶的只有[0, ULIM)大概3.7GB而非4GB的空間(ULIM = 0xef800000),其余的空間需要分配給操作系統。