设为首页 加入收藏

TOP

Linux内核源码学习之 内核页表打印(一)
2015-04-07 15:29:54 来源: 作者: 【 】 浏览:58
Tags: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_k

首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
分享到: 
上一篇C#函数式编程之序列 下一篇Java访问模式(Visitor者模式)

评论

帐  号: 密码: (新用户注册)
验 证 码:
表  情:
内  容: