第2章 存儲管理
LINUX頁式管理
PGD PMD PT PTE
頁表目標 中間目錄 頁表 頁表項
LINUX在32位地址下采取二層映射
#define PGDIR_SHIFT 22
#define PTRS_PER_PGD 1024
#define PMD_SHIFT 22
#define PTRS_PER_PMD 1
#define PTRS_PER_PTE 1024
根據以上宏定義,PMD被完美的架空了,而相當于采取了二層映射
其中PGD用了線性地址的最高10位 與 MMU 對應
線性地址的中間10位是所對應的PTE在PT中的索引
剩下的最低12位則是頁中的偏移量
虛擬地址 = 段基地址:段偏移量
16位 32位
更準確的講是段選擇子了吧
在LINUX中段基地址 = 0(下面的____KERNEL_CS等),所以可以認為線性地址與虛擬地址總是相等的,但其本質不是一個東西
0xC0000000-0xFFFFFFFF為內核占用
0x0-0xBFFFFFFF為用戶控件
內核的虛擬內存為簡單的線性映射
#__PAGE_OFFSET (0xC0000000)
#define PAGE_OFFSET ((unsigned long) __PAGE_OFFSET)
#define __pa(x) ((unsigned long)(x) - PAGE_OFFSET)
#define __va(x) ((void *)((unsigned long)(x) +PAGE_OFFSET)
__pa是從虛擬地址轉換成物理地址
__va是從物理地址轉換成虛擬地址
在GDT中有4個段描述符
其索引是2-5
分別是
__KERNEL_CS 內核代碼段
__KERNEL_DS 內核數據段
__USER_CS 用戶代碼段
__USER_DS 用戶數據段
#define start_thread(regs,new_eip,new_esp) do {\
__asm__("movl %0,%%fs;movl %0,%%gs"::"r"(0)); \
set_fs(USER_DS);
regs->xds = __USER_DS; \
regs->xes = __USER_DS; \
regs->xss = __USER_DS; \
regs->xcs = __USER_CS; \
regs->eip = new_eip; \
regs->esp = new_esp; \
}while(0)
通過這段宏可以看出,LINUX沒用段式存儲,雖然它也走了這個流程
MMU的流程 MMU使用物理地址
頁式映射
從REG CR3拿PGD的地址
找到頁面目錄,線性地址中的高10位為索引,找到頁面目錄項,從中拿高20位作為頁面表的索引,頁面表與4k字節邊界對齊,CPU自動補充前12位為0得到頁面表地址。
然后拿線性地址的中間10位,得到頁面表中的索引,拿到頁面表項,頁面表項的高20位在低位補充12個0,再加上線性地址的低12位組成物理地址。
mm_struct 任務相關的虛擬內存
vm_area_struct 一段虛擬內存的抽象,也可以理解為段
mm_struct中擁有vm_area_struct的指針
在vm_area_struct多的時候使用avl樹來存儲
mem_map_t 物理頁表
zone_struct 物理內存的區結構,zone_struct把物理內存分成了幾個部分
ZONE_DMA 0 供DMA使用
ZONE_NORMAL 普通使用
ZONE_HIGHMEN 高段內存,內核映射不到
物理內存之間區的劃分并不是強制的,如果某一個區已經沒有內存可用,是可以去別的區拿內存的
其實一直對內核的尋址有些疑問
不過剛剛似乎想通了
內核會做預映射,把PGD第768項以后的都做映射,也就是1G的空間
而這種映射應該是滿足__pa()宏,即線性地址與物理地址是線性映射的。
所以最終__pa()宏被用作在內核代碼中顯性的獲得某個線性地址所對應的物理地址
而MMU負責把一個線性地址隱式的轉成了物理地址,而這已轉換與內核代碼無關。
不知這樣理解是否正確?
今天只看到了這里
待續……
說起來把這么個東西放到首頁很不好意思,主要目的是希望有看到的人幫我指正一下我所認知的錯誤或者解惑。謝謝啦:)