歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux綜合 >> Linux內核 >> Linux內核源碼學習之 內核頁表打印

Linux內核源碼學習之 內核頁表打印

日期:2017/3/1 9:31:26   编辑:Linux內核

本學期Linux內核實驗最後是打印內核頁表,線性地址----物理地址

我看到這個實驗題目的時候想到的就是這個init函數(因為這部分當時就是我講的^_^),這個函數是初始化linux內核頁表的,也就是將32位系統中3G以上的896M線性地址映射到物理地址的0-896M,在其調用者paging_init函數中還處理了其他的情況,比如固定映射之類的。那屬於高端內存映射那一塊的內容,目前我們先看一下如何將內核頁表3G~3G+896M的線性地址對應的物理地址打印出來。

一下的源碼是linux2.6.11版本的,應該是和白皮書上的是對應的版本

static void __initkernel_physical_mapping_init(pgd_t *pgd_base)

{

unsignedlong pfn;

pgd_t*pgd;

pmd_t*pmd;

pte_t*pte;

intpgd_idx, pmd_idx, pte_ofs;

/*因為內核的線性地址空間是從0xC0000000開始的,所以這裡我們只需要初始化內核全局頁目錄從0x300開始的項*/

pgd_idx= pgd_index(PAGE_OFFSET); /*768*/

pgd= pgd_base + pgd_idx; /*pgd指向當前的目錄項 */

pfn= 0; /*需要被映射的物理頁框號,從物理地址0開始 */

/*初始化從768開始的每個頁全局目錄項,PTRS_PER_PGD為總項數1024 */

for(; pgd_idx < PTRS_PER_PGD; pgd++, pgd_idx++) {

pmd= one_md_table_init(pgd);

if(pfn >= max_low_pfn) /*max_low_pfn代表被內核直接映射的最後一個物理頁框的頁框號*/

continue;

/*初始化每個頁中間目錄項,前面說到啟用了物理地址擴展的32位x86系統中,使用三級映射,

而沒有啟用物理地址擴展的32位系統,其實只使用了其中的兩級,雖然在軟件結構中PMD依然存在,

但實際只是一個擺設。內核通過將PTRS_PER_PMD設為1,並且在one_md_table_init初始化PMD的函數中

直接將PMD的第一項初始化為指向其地址的PGD項本身,完成了一個"原地"的映射。也就是說,

此時的每一個頁目錄項,既表示一個頁中間目錄描述符,也表示一個頁表 */

for(pmd_idx = 0; pmd_idx < PTRS_PER_PMD && pfn < max_low_pfn; pmd++,pmd_idx++) {

unsignedint address = pfn * PAGE_SIZE + PAGE_OFFSET;

/*Map with big pages if possible, otherwise create normal page tables. */

if(cpu_has_pse) {

unsignedint 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;

}else {

pte= one_page_table_init(pmd);

/*最後初始化每個頁表項,也就是每個物理頁框的描述符。注意pfn++表示頁框號依次加1,

而其初始值為0,也就是把物理地址從0開始的頁框,直接映射到內核線性地址0xC0000000開始的空間內

,映射的方式與臨時內核頁表相似,只不過范圍更大了 */

for(pte_ofs = 0;

pte_ofs < PTRS_PER_PTE && pfn< max_low_pfn;

pte++, pfn++, pte_ofs++, address +=PAGE_SIZE) {

if(is_kernel_text(address))

set_pte(pte,pfn_pte(pfn, PAGE_KERNEL_EXEC));

else

set_pte(pte,pfn_pte(pfn, PAGE_KERNEL));

}

}

}

}

}

注:上面的注釋是針對沒有開啟PAE模式的,如果開啟PAE,就是 4----512----512----也就是上面的768變為3,PTRS_PER_PGD為512 PTRS_PER_PMD為1,PTRS_PER_PTE為512

看完這個函數,我們來想一下怎麼打印內核頁表,我的系統是默認開啟PAE的,也就是內存中是2M的頁和4KB的頁並存的,頁目錄和頁表的組織形式是:

Cr3--àPDPT--àpmd [--àpte]-àpage 中間的pte之所以要加方括號,表示的是這一級不一定有,即如果對應的是2M的頁,那麼這個pmd中存放的就是2M頁的物理地址和標志位,此時page的大小是2M;如果有這一級,那麼對應的頁是4KB的。這個從數量上很好理解,pmd一個表項對應的是2M的線性地址空間,如果頁的大小是2M那麼pmd相當於是頁表,如果頁的大小是4KB,那麼有512項,正好需要借助再加上一級的pte(512)項來表示,這也就是上面函數對應的在pmd下判斷是否有2M頁的結構。

所以打印函數可以寫成這樣了:

for (; pgd_idx < PTRS_PER_PGD;pgd_idx++) {

unsignedlong pgd_cur = pgd_idx * PGDIR_SIZE;

pmd= pmd_offset((pud_t *)(pgd_base + pgd_idx), pgd_cur);

for(pmd_idx = 0; pmd_idx < 448/*PTRS_PER_PMD*/; pmd_idx++) {

unsignedlong pmd_cur = pgd_cur + pmd_idx * PMD_SIZE;

if(pmd_present(pmd[pmd_idx])){

pte= pte_offset_kernel((pmd_t *)(pmd + pmd_idx), pmd_cur);

pte1= pmd_val(*((pmd_t *)(pmd + pmd_idx)));

if((((unsignedlong)pte1)>>7)&0x1)

{

//打印2M頁

}

else

{

for(pte_ofs = 0; pte_ofs < PTRS_PER_PTE; pte_ofs++) {

unsignedlong pg_cur = pmd_cur + pte_ofs * PAGE_SIZE;

if(pte_present(pte[pte_ofs]))

{

//打印4kb頁

}

}

}

}

}

}

因為打印的內容很多,用seq文件實現比較方便。

Copyright © Linux教程網 All Rights Reserved