# 七、
來源:[JOS學習筆記(七)](http://blog.csdn.net/roger__wong/article/details/8716419)
接前一篇。
上篇日志主要是完成了一些分頁相關機制的工作,但還沒有真正的去使用這個分頁系統。Lab2的part3部分主要就是讓我們使用part2中完成的映射機制來初始化內核的頁目錄和頁表,并將此頁目錄加載到cr3里,讓os真正去使用我們初始化之后的頁目錄以取代kernpgdir.c里面簡單的頁目錄。
在開始之前讓我們看一下JOS的虛擬內存分布圖,在part3里的所有工作就是照著此圖實現其中的部分映射。

首先從高位地址說起,kernbase到最高為4g是一塊remapped內存,這塊很大的內存從低到高要映射整塊物理內存。
其次kernbase往下PTSIZE(貌似是4M)是無用內存,再往下是KERNSTACKTOP也就是內核棧的棧頂,眾所周知棧的生長順序是從高地址向低地址生長,所以這個位置也就是棧開始生長的位置。從KERNSTACKTOP往下KSTKSIZE為內核棧區域,大概是8個頁面,內核棧不能超過這個區域。從KERNSTACKTOP往下一個PTSIZE這塊區域除了內核棧之外還有一塊空白區域,防止棧溢出的時候復寫用戶空間的數據。
接著是ULIM,代表用戶空間的最高地址,換句話說此位置往下所有空間為用戶空間,用戶空間有很多東西,之后的LAB會詳細說明,在此只說UPAGES。
UPAGES是JOS用戶記錄物理頁面使用情況的數據結構,為了使用戶空間能訪問這塊數據結構,會將PAGES映射到UPAGES位置。
好總結一下,是要總共要求映射3塊區域,,1是映射UPAGES結構,,2是映射內核棧,3映射整塊物理內存。
## 1、UPAGES的映射
所謂映射就是將某個虛擬內存地址(符號地址)映射到實際的物理地址,UPAGES映射到pages也就是通過UPAGES訪問到的地址恰恰是pages這個符號指向的物理地址的內容。而映射的實質就是往頁表和頁目錄里寫入相應的值,使UPAGES這個符號經過地址變換之后指向的地址恰好是pages的物理地址。
實現的方式很簡單,先獲取pages數組的大小,并向上對齊到PGSIZE得到要映射的頁面,然后一個頁面一個頁面分別映射即可。
容易出錯的地方:(1)不能使用sizeof獲取pages數組的大小,因為此數組是動態分配的,使用sizeof只會獲取到pages這個指針本身的大小(4B)。(2)一個頁面地址映射一次即可,映射的最小單位是頁面,一個頁面內的地址不需要反復映射。

## 2、內核棧的映射。
目前內核棧的符號地址是bootstack表示棧底(棧的低地址,不是邏輯上的棧底反而是邏輯上的棧頂,因為向下生長),需要將KSTACKTOP-KSTKSIZE映射到這個位置,并向上按順序映射KSTKSIZE大小的內存區域。
其余部分不用管,雖然函數說明中寫了很多。

## 3、映射整塊內存區域。
要映射KERNBASE到0xffffffff總共256M內存的空間到從0開始的物理地址。雖然我們沒有足夠的物理空間,但we just set up the mapping anyway。什么叫just set up the mapping anyway啊,沒有就是沒有,不夠就是不夠,你讓我映射到哪里去?!抱怨歸抱怨,但我都給映射到了物理的0地址上去了,也算是沒有辦法的辦法。
但這么映射整塊物理內存會出問題。在page_insert操作中會將該Page結構的pp_ref++,但映射整塊物理內存的過程實際是便利了整個pages數組,這就會導致所有的Page結構的pp_ref++。但別忘了還有個page_free_list串起來了空閑的Page,這些空閑的Page理所應當的pp_ref也被增加了,最后的結果是page_free_list里面頁面的pp_ref不為0。在空閑鏈表中的頁面的引用次數不為0,這很明顯是不符合邏輯的,因此要進行--操作。
當然還有一種解釋是:不這么做通不過check,至于愿意相信哪種隨你好了。
通過這個問題我覺得這算是JOS設計不合理的一個地方,映射和頁面被引用是否是一個概念?誰負責維護pp_ref的信息,是這個頁面的申請者還是page_insert函數還是page_insert的調用者?感覺這些地方還有待商榷。但之前1和2的映射不用考慮這個問題是因為牽扯到的頁面不在free_page_list里,這在free_page_list初始化的時候就把里面的頁面剔除了。

最后,可以看到通過check。

lab2到此結束,lab2和lab1的代碼我上傳到自己的資源里歡迎下載。