與直接映射的物理內存末端、高端內存的始端所對應的線性地址存放在high_memory變量中,在x86體系結構上,高於896MB的所有物理內存的范圍大都是高端內存,它並不會永久地或自動地映射到內核地址空間,盡管x86處理器能夠尋址物理RAM的范圍達到4GB(啟用PAE可以尋址到64GB)。一旦這些頁被分配,就必須in射到內核的邏輯地址空間上。在x86上,高端內存中的頁被映射到3GB-4GB。
內核可以采用三種不同的機制將頁框映射到高端內存;分別叫做永久內核映射、臨時內核映射以及非連續內存分配。在這裡,只總結前兩種技術,第三種技術將在後面總結。
建立永久內核映射可能阻塞當前進程;這發生在空閒頁表項不存在時,也就是在高端內存上沒有頁表項可以用作頁框的“窗口”時。因此,永久內核映射不能用於中斷處理程序和可延遲函數。相反,建立臨時內核映射絕不會要求阻塞當前進程;不過,他的缺點是只有很少的臨時內核映射可以同時建立起來。
使用臨時內核映射的內核控制路徑必須保證當前沒有其他的內核控制路徑在使用同樣地映射。這意味著內核控制路徑永遠不能被阻塞,後者其他內核控制路徑有可能使用同一個窗口來映射其他的高端內存頁。
永久內存映射
永久內核映射允許內核建立高端頁框到內核地址空間的長期映射。他們使用住內核頁表中一個專門的頁表,其地址存放在變量pkmap_page_table中,這在前面的頁表機制管理區初始化中已經介紹過了。頁表中的表項數由LAST_PKMAP宏產生。因此,內核一次最多訪問2MB或4MB的高端內存。
[cpp]
- /*這裡由定義可以看出永久內存映射為固定映射下面的4M空間*/
- #define PKMAP_BASE ((FIXADDR_BOOT_START - PAGE_SIZE * (LAST_PKMAP + 1)) \
- & PMD_MASK)
該頁表映射的線性地址從PKMAP_BASE開始。pkmap_count數組包含LAST_PKMAP個計數器,pkmap_page_table頁表中的每一項都有一個。
高端映射區邏輯頁面的分配結構用分配表(pkmap_count)來描述,它有1024項,對應於映射區內不同的邏輯頁面。當分配項的值等於0時為自由項,等於1時為緩沖項,大於1時為映射項。映射頁面的分配基於分配表的掃描,當所有的自由項都用完時,系統將清除所有的緩沖項,如果連緩沖項都用完時,系統將進入等待狀態。
[cpp]
- /*
- 高端映射區邏輯頁面的分配結構用分配表(pkmap_count)來描述,它有1024項,
- 對應於映射區內不同的邏輯頁面。當分配項的值等於零時為自由項,等於1時為
- 緩沖項,大於1時為映射項。映射頁面的分配基於分配表的掃描,當所有的自由
- 項都用完時,系統將清除所有的緩沖項,如果連緩沖項都用完時,系
- 統將進入等待狀態。
- */
- static int pkmap_count[LAST_PKMAP];
- /*last_pkmap_nr:記錄上次被分配的頁表項在pkmap_page_table裡的位置,初始值為0,所以第一次分配的時候last_pkmap_nr等於1*/
- static unsigned int last_pkmap_nr;
為了記錄高端內存頁框與永久內核映射包含的線性地址之間的聯系,內核使用了page_address_htable散列表。該表包含一個page_address_map數據結構,用於為高端內存中的每一個頁框進行當前映射。而該數據結構還包含一個指向頁描述符的指針和分配給該頁框的線性地址。
[cpp]
- * Hash table bucket
- */
- static struct page_address_slot {
- struct list_head lh; /* List of page_address_maps */
- spinlock_t lock; /* Protect this bucket's list */
- } ____cacheline_aligned_in_smp page_address_htable[1<<PA_HASH_ORDER];
-
- /*
- * Describes one page->virtual association
- */
- struct page_address_map {
- struct page *page;
- void *virtual;
- struct list_head list;
- };
page_address()函數返回頁框對應的線性地址
[cpp]
- * Returns the page's virtual address.
- */
- /*返回頁框對應的線性地址*/
- void *page_address(struct page *page)
- {
- unsigned long flags;
- void *ret;
- struct page_address_slot *pas;
- /*如果頁框不在高端內存中*/
- if (!PageHighMem(page))
- /*線性地址總是存在,通過計算頁框下標
- 然後將其轉換成物理地址,最後根據相應的
- 物理地址得到線性地址*/
- return lowmem_page_address(page);
- /*從page_address_htable散列表中得到pas*/
- pas = page_slot(page);
- ret = NULL;
- spin_lock_irqsave(&pas->lock, flags);
- if (!list_empty(&pas->lh)) {/*如果對應的鏈表不空,
- 該鏈表中存放的是page_address_map結構*/
- struct page_address_map *pam;
- /*對每個鏈表中的元素*/
- list_for_each_entry(pam, &pas->lh, list) {
- if (pam->page == page) {
- ret = pam->virtual;/*返回線性地址*/
- goto done;
- }
- }
- }
- done:
- spin_unlock_irqrestore(&pas->lock, flags);
- return ret;
- }