JOS Lab2 Memory Management Part 3 & challenge

潘樾陽發表於2020-10-26

Part 3: Kernel Address Space

JOS 把處理器的32位地址空間劃分為兩部分。User Environment管理低地址的部分,Kernel則對高地址的部分保持控制。這種劃分比較的任意,大致是以inc/memlayout,h中的ULIM為劃分。
因為kernel和user memory在彼此的地址空間中存在,我們需要使用許可權位來讓使用者程式碼只能訪問使用者的地址空間。不然可能會導致寫入了核心程式碼而產生問題。
使用者態應當沒有許可權來訪問在ULIM上的記憶體,kernel則應當擁有讀寫許可權。使用者態程式碼和核心都對[UTOP, ULIM)的記憶體只擁有讀許可權,因為這部分是為了向使用者態暴露一部分核心只讀資料結構。UTOP下面的地址空間則是留給使用者態使用的。

初始化核心地址空間

Exercise 5要求設定UTOP以上的地址空間,補充完成mem_init()中剩下內容。

	//
	// Map 'pages' read-only by the user at linear address UPAGES
	// Permissions:
	//    - the new image at UPAGES -- kernel R, user R
	//      (ie. perm = PTE_U | PTE_P)
	//    - pages itself -- kernel RW, user NONE
	// Your code goes here:
	boot_map_region(kern_pgdir, UPAGES, PTSIZE, PADDR(pages), PTE_U);

	//
	// Use the physical memory that 'bootstack' refers to as the kernel
	// stack.  The kernel stack grows down from virtual address KSTACKTOP.
	// We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
	// to be the kernel stack, but break this into two pieces:
	//     * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
	//     * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
	//       the kernel overflows its stack, it will fault rather than
	//       overwrite memory.  Known as a "guard page".
	//     Permissions: kernel RW, user NONE
	// Your code goes here:
	boot_map_region(kern_pgdir, KSTACKTOP - KSTKSIZE, KSTKSIZE, PADDR(bootstack), PTE_W);

	//
	// Map all of physical memory at KERNBASE.
	// Ie.  the VA range [KERNBASE, 2^32) should map to
	//      the PA range [0, 2^32 - KERNBASE)
	// We might not have 2^32 - KERNBASE bytes of physical memory, but
	// we just set up the mapping anyway.
	// Permissions: kernel RW, user NONE
	// Your code goes here:
	boot_map_region(kern_pgdir, KERNBASE, 0x10000000, 0, PTE_W);

然後make qemu看看結果:

Physical memory: 131072K available, base = 640K, extended = 130432K
check_page_free_list() succeeded!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
check_page_alloc() succeeded!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
Fail to allocate a new page! Out of Free Physical Memory!
check_page() succeeded!
check_kern_pgdir() succeeded!
check_page_free_list() succeeded!
check_page_installed_pgdir() succeeded!

通過了所有測試。
在回答後面的問題之前,我們重新回過頭梳理一下這個lab究竟做了什麼事情,完成了怎樣的對映。

Review:

先來回顧一下各個函式究竟在幹什麼,特別關注操作物件及其地址情況。
boot_alloc:
給定n,在.bss end (0xf011A0000)之上劃分一塊n大小,按頁對齊的虛擬地址空間。
page_init:
對虛擬地址[0xf011B000, 0xf015B000)pages陣列根據實體記憶體的使用情況進行初始化,產生一個PageInfo的空閒實體記憶體頁的連結串列。
page_alloc:
給定alloc_flags(決定分配的頁是否要清零),從PageInfo的空閒實體記憶體頁的連結串列中找到代表空閒物理頁的PageInfo結構體,並將這個結構體對應的虛擬記憶體頁根據alloc_flag清零或不清零,返回指向這個結構體的指標。
page_free:
將給定的PageInfo結構體加入空閒連結串列中。這裡即不對實體記憶體做操作,也不對虛擬記憶體做操作,也不調整頁表項。
pgdir_walk:
給定指向頁目錄的指標和虛擬地址,返回對應虛擬地址的頁表項的指標。這裡特別需要注意虛擬地址和實體地址的區分。拿到的頁目錄的指標是虛擬地址,得到的頁目錄項中包括了頁表的實體地址。需要先轉換成頁表的虛擬地址,然後訪問拿到對應的頁表項,再取出頁表項的虛擬地址,返回。
page_lookup:
給定指向頁目錄的指標,一個虛擬地址,和一個指向頁表項指標的指標。首先查詢該虛擬地址對應的頁表項,如果不存在指向該頁表項的指標,或者該頁表項不是Present返回NULL,否則傳入的雙重指標指向該頁表項的指標,返回指向對應該頁表項對應實體地址的PageInfo結構體的指標,並且使虛擬地址對應的TLB無效。
page_remove:
給定指向頁表項的指標,一個虛擬地址。先查詢並拿到虛擬地址對應的PageInfo結構體和指向該虛擬地址對應頁表項的指標。減少對該物理頁的引用並清空該頁表項,相當於取消虛擬地址和其對應實體地址的對映。
page_insert:
給定指向頁目錄的指標,一個代表物理頁的指向PageInfo結構體的指標,和一個虛擬地址。先找到該虛擬地址對應的頁表項的指標,如果該指標存在且頁表項Present,那麼我們先增加對給定物理頁的引用,使用page_remove移除原有的對映(順序是針對corner case的考慮),拿到給定結構體代表的物理頁的實體地址,寫到給定虛擬地址對應的頁表項裡,並使該虛擬地址對應的TLB無效。
boot_map_region:
給定指向頁目錄的指標,一個起始虛擬地址,一個起始實體地址,和地址空間大小以及許可權。取出虛擬地址對應的頁表項,把對應的實體地址和許可權寫進去。注意在註釋中說不需要增加相應物理頁的引用。(感謝GZZ同學解惑,我之前這裡還不是特別清楚為什麼不用引用計數增加,因為這裡並不是真正地分配了page,而僅僅是通過寫頁表完成了對映)
這裡看的時候還需要特別注意,KADDRpage2kva的區別,以及PADDRpage2pa的區別。前者是直接返回某個虛擬地址的實體地址或者是某個實體地址的虛擬地址,即加減KERNBASE,而後者則是返回PageInfo結構體對應的實體地址和虛擬地址。
我們再回去看mem_init(),對映完成之後就是這麼個情況:
在這裡插入圖片描述然後我們來回答一下剩下的問題:
2. (補坑)
3. 因為使用了許可權位,因為核心程式碼的許可權位標誌為supervisor level且Read-only access
4. 最多支援2G,因為RO PAGES 4096*1024 Byte 一個PageInfo結構為8 Byte,一個物理頁4096 Byte,因此最多訪問4096 * 1024 / 8 * 1024 = 2GB
5. 最多可以放4MB的PageInfo陣列,加上4KB的頁目錄,再加上4MB的頁表,一共8MB+4KB
6. 在jmp *%eax之後。 因為我們之前在entrypgdir.c裡面把[0x00000000, 0x00400000)的虛擬地址對映到了[0x00000000, 0x00400000)的實體地址。因為後面kern_pgdir會被載入進來,低虛擬地址就不再使用了。

Challenge

(補坑)

相關文章