在上一节中,我们可以看到,在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