得到了總的頁面數
max_pfn
和高階頁面數
highmem_pages
之後,來到
setup_arch
的947
行,呼叫
函式來建立系統初始化階段的臨時分頁體系,傳入的引數意義代表從
0~max_low_pfn
對應的32
位實體地址(低
12位全為
0,也就是頁面對齊)
,在函式
函式中先後呼叫下面的幾個函式來設定記憶體相關資料(因為
bootmem
此時沒有初始化):
find_early_table_space()
early_ioremap_page_table_range_init()
load_cr3()
reserve_early()
其中,首先
find_early_table_space
所實現的功能是相當重要的:
32static void __init find_early_table_space(unsigned long end, int use_pse,
33int use_gbpages)
34 else
46pmds = (end + pmd_size - 1) >> pmd_shift;
47 48tables += roundup(pmds * sizeof(pmd_t), page_size);
49 50if (use_pse) else
59ptes = (end + page_size - 1) >> page_shift;
60 61tables += roundup(ptes * sizeof(pte_t), page_size);
62 63#ifdef config_x86_32
64/* for fixmap */
65tables += roundup(__end_of_fixed_addresses * sizeof(pte_t), page_size);
66#endif
67 68/*
69* red-pen putting page tables only on node 0 could
70* cause a hotspot and fill up zone_dma. the page tables
71* need roughly 0.5kb per gb.
72*/
73#ifdef config_x86_32
74start = 0x7000;
75#else
76start = 0x8000;
77#endif
79tables, page_size);
80if (e820_table_start == -1ul)
81panic("cannot find space for the kernel page tables");
82 83e820_table_start >>= page_shift;
84e820_table_end = e820_table_start;
85e820_table_top = e820_table_start + (tables >> page_shift);
86 88end, e820_table_start << page_shift, e820_table_top << page_shift);
89}
我們看到它確定了
pud和
pmd以及
pte、固定記憶體對映等所有的選項所使用的記憶體空間的大小;之後呼叫
find_e820_area
函式從e820.map
陣列中尋找到一塊能夠容納所有頁表項的記憶體段:
743u64 __init find_e820_area(u64 start, u64 end, u64 size, u64 align)
744 763return -1ull;
764}
find_e820_area
中檢測e820.map
的每乙個元素,這個元素代表的記憶體區必須是
e820_ram
,然後呼叫
find_early_area
獲得tables
539u64 __init find_early_area(u64 ei_start, u64 ei_last, u64 start, u64 end,
540u64 size, u64 align)
541
這個find_early_area
函式我們要好好說道說道。早系統初始化時,什麼記憶體管理器、記憶體模型這些東西都沒有建立,那麼獲得記憶體的最低階函式就是這個
find_early_area
函式。它接收
6個引數:
ei_start
和ei_last
表示分配範圍,我們看到這個範圍不能超出某個
e820.map[i]
元素代表的範圍;
start
和end
代表期望分配的範圍;
size
為期望分配大小;
align
表示對齊方式。我們看到,如果
addr >= ei_last
,或者last > ei_last
,或者last > end
都會分配失敗。這個條件看上去很苛刻,不過
e820.map
陣列有那麼多元素,總有乙個元素會滿足的,不然所有的
linux
都無法執行了。
round_up
以及還有乙個雙胞胎弟弟
round_down
的定義如下:
#define __round_mask(x, y) ((__typeof__(x))((y)-1))
#define round_up(x, y) ((((x)-1) | __round_mask(x, y))+1)
#define round_down(x, y) ((x) & ~__round_mask(x, y))
獲得了獲得頁表
tables
的首位址之後,
find_early_table_space
就用它設定
e820_table_start
、e820_table_end
和e820_table_top
的值。這三個全域性變數相當的重要,分別表示這個將要容納所有核心頁表的空間的起始、結束和頂部頁對齊位址頭
20位(此時
e820_table_start
和e820_table_end
是相等的),在後面的頁表初始化階段將會使用到這三個變數。
看了這麼多**,很多同學可能有些迷糊了。為啥我們在
arch/x86/kernel/head_32.s
中已經初始化了分頁環境,建立了乙個臨時頁表了,為啥這裡還要建乙個呢?其實,這就是
ulk-3
書中提到的核心臨時頁表和最終核心頁表的概念。我在部落格「高階記憶體對映」
中也提到過這一點。
_end
的位置,也就是把解壓縮後的核心**對映出來了。而這裡,是要把所有可用的
ram(注意這裡是包括了已經映**的核心**、頁目錄)以頁為單位分成多個頁,每個頁乙個位元,提供乙個初始階段記憶體的分配和釋放管理平台。
目前我的電腦,在
arch/x86/kernel/head_32.s
裡只是映**大概4m的
vmlinux
**。核心尺寸在
4m左右
(不壓縮
),一般需要連續對映
3個頁面表。現在要把所有
ram對映到核心空間。那麼核心要根據
e820
物理記憶體的布局,也就是
ram的結點布局對多個結點及結點的管理區作初化,最終把除去核心之外所有剩餘的頁交給頁框分配器,同時也完成了頁框分配器的初始化。
核心頁表和程序頁表
初學核心時,經常被 核心頁表 和 程序頁表 搞暈,不知道這到底是個啥東東,跟我們平時理解的頁表有和關係 核心頁表 程序頁表 每個程序自己的頁表,放在程序自身的頁目錄task struct.pgd中。在保護模式下,從硬體角度看,其執行的基本物件為 程序 或執行緒 而定址則依賴於 程序頁表 在程序排程而...
Linux核心頁表
一 linux位址空間 arm的32位系統共支援4g的記憶體空間,其中0 3g為使用者空間,3g 4g是核心空間,arm採用2級頁表,32位位址空間address分別為 pgd pte 12bits,在核心 中分別為pgd 11位,pte 9 位,頁內位址12位 但是在mmu系統中對於arm的二級分...
關於使用者程序頁表和核心頁表
普通使用者程序的頁表也是存在核心空間的。這很容易理解,畢竟頁表沒有vma來對應。在應用程序建立的時候,task struct m struct描述記憶體資訊,mm gpd指定頁表基位址。頁表的分配是通過呼叫核心夥伴演算法介面分配到物理記憶體,核心在啟動階段已經建立了核心頁表,使用者程序的頁表可以分為...