核心的最終頁表
在上一節中,我們可以看到,在kernel/head.S中建立了臨時頁表swapper_pg_dir。
- 建立低端記憶體kernel_physical_mapping_init
static void __init kernel_physical_mapping_init(pgd_t *pgd_base) 144 { 145 unsigned long pfn; 146 pgd_t *pgd; 147 pmd_t *pmd; 148 pte_t *pte; 149 int pgd_idx, pmd_idx, pte_ofs; 150 151 pgd_idx = pgd_index(PAGE_OFFSET); //0x38000 152 pgd = pgd_base + pgd_idx; / /swapper_pg_dir+0x300 153 pfn = 0; 154 155 for (; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) { 156 pmd = ·(pgd); //pmd獲取頁目錄項的一個項的地址,也就是變數pgd的值。 158 if (pfn >= max_low_pfn) 159 continue; 160 for (pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++, pmd_idx++) { 161 unsigned int address = pfn * PAGE_SIZE + PAGE_OFFSET; 163 /* Map with big pages if possible, otherwise create normal page tables. */ 164 if (cpu_has_pse) { 165 unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1; 166 167 if (is_kernel_text(address) || is_kernel_text(address2)) 168 { 169 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC)); 171 } 172 else 173 { 174 set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE)); 176 } 177 pfn += PTRS_PER_PTE; 178 } else { 179 pte = one_page_table_init(pmd); //為目錄項的一個項分配一個頁,然後把這個目錄項的一個項指向這個頁表地址。 181 182 for (pte_ofs = 0; pte_ofs < PTRS_PER_PTE && pfn < max_low_pfn; pte++, pfn++, pte_ofs++) { //因為max_low_pfn=0x38000,一共建立了0x38000個頁,每個頁大小是4k, //則0x38000*4K=0x380M=896M, // 這樣就完成了,核心地址從0xC000 0000-0xC37F FFFF對映到實體地址0-0x37F FFFF這樣核心地址就能訪問到低896M記憶體。 183 if (is_kernel_text(address)) 184 { 185 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL_EXEC)); 187 } 188 else 189 { 190 set_pte(pte, pfn_pte(pfn, PAGE_KERNEL)); //初始化頁表,地址從0<<12,1<<12填充頁表 ,pte是虛擬地址,然後pfn_pte(pfn, PAGE_KERNEL)這個高20位,然後再補12位0,就是實體地址.. } 193 } 194 } 195 } 196 }
這個函式建立後,會有如下結構圖:
1)使用者空間的線性地址可以對映到8M
2) 核心空間地址可以對映到896M。
PSE的開啟
在arch/i386/kernel/cpu/common.c中
264 cpuid(0x00000001, &tfms, &junk, &excap, &capability);
265 c->x86_capability[0] = capability;
140 static inline void cpuid(int op, int *eax, int *ebx, int *ecx, int *edx) 141 { 142 __asm__("cpuid" 143 : "=a" (*eax), 144 "=b" (*ebx), 145 "=c" (*ecx), 146 "=d" (*edx) 147 : "0" (op), "c"(0)); 148 }
就是通過呼叫,cpuid彙編,然後把capabilty的值儲存到edx中,然後c->x86_capability[0] 等於edx,裡面有了包含CPU是否支援PSE。
PSE的開啟與關閉
CR4中的PSE位預設是0.
開啟PSE功能。
- 置位PDE中的PS
- 置位CR4的PSE
置位PDE中的PS位的動作在arch/i386/mm/init.c中
if (cpu_has_pse) { unsigned int address2 = (pfn + PTRS_PER_PTE - 1) * PAGE_SIZE + PAGE_OFFSET + PAGE_SIZE-1; if (is_kernel_text(address) || is_kernel_text(address2)) { set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE_EXEC)); } else { set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE)); } pfn += PTRS_PER_PTE; }
set_pmd(pmd, pfn_pmd(pfn, PAGE_KERNEL_LARGE))可以看到置位PDE中的PS位。
置位CR4的PSE在pagetable_init中
342 if (cpu_has_pge) {
343 set_in_cr4(X86_CR4_PGE);
344 __PAGE_KERNEL |= _PAGE_GLOBAL;
345 __PAGE_KERNEL_EXEC |= _PAGE_GLOBAL;
346 }
PSE的關閉
1.
可以通過disable_pse=1,然後
在arch/i386/kernel/cpu/common.c的early_cpu_init中新增下面語句:
529 void __init early_cpu_init(void)
530 {
...................
540 early_cpu_detect();
541
542 if(disable_pse)
543 clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability);
544
545 #ifdef CONFIG_DEBUG_PAGEALLOC
546 /* pse is not compatible with on-the-fly unmapping,
547 * disable it even if the cpus claim to support it.
548 */
549 clear_bit(X86_FEATURE_PSE, boot_cpu_data.x86_capability);
550 disable_pse = 1;
551 #endif
.....
}
2.或者開啟kernel_hacking----------->Page alloc debugging
這樣就關閉了PSE
if (disable_pse)
clear_bit(X86_FEATURE_PSE, c->x86_capability);
這樣清空了x86_capability中的86_FEATURE_PSE。CR4中的PSE預設是0,PDE也沒置位PS。
MMU單元是按大頁(4M)或者頁(4K)轉換線性地址,主要根據:
1)PDE中的PS
2)CR4的PSE是否開啟
關於地址的一些說明
設定好CR3,然後硬體MMU單元會把線性地址轉化為實體地址。
1)開啟分頁後,彙編還是c用的地址都是線性地址,都會經過MMU單元轉化為實體地址。
2)CR3暫存器裡,頁目錄,和頁表中存放的都是實體地址,因為MMU會用到CR3頁表,頁必須都是實體地址。
movl $swapper_pg_dir-__PAGE_OFFSET,%eax
movl %eax,%cr3 /* set the page table pointer.. */
所以:
pte_t *page_table = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);
set_pmd(pmd, __pmd(__pa(page_table) | _PAGE_TABLE));
注意:set_pmd中,pmd是虛擬地址,__pa(page_table)是實體地址。
問題:
根據框圖,使用者空間和核心空間的地址空間是多少?
核心的頁表項,是768和769項?
這他們能定址:
1100 0000 00 00 0000 0000 00000 0000 0000—1100 0000 00 11 1111 1111 1111 1111 1111
也就是0xC00 0000-0xC03 fffff
1100 0000 01 00 0000 0000 00000 0000 0000—1100 0000 01 11 1111 1111 1111 1111 1111
也就是0xC04 0000-0xC07f ffff
也就是:核心的地址0xC00 0000- 0xC03 ffff,對映到實體地址0-0x7f ffff,也是能定址8M