内核的最终页表

在上一节中,我们可以看到,在kernel/head.S中建立了临时页表swapper_pg_dir。

  1. 建立低端内存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         } 

这个函数建立后,会有如下结构图:
123
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功能。

  1. 置位PDE中的PS
  2. 置位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

猜你喜欢

转载自blog.csdn.net/chengbeng1745/article/details/84833242