1. 程式人生 > >核心的最終頁表

核心的最終頁表

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