Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2150563
  • 博文数量: 438
  • 博客积分: 3871
  • 博客等级: 中校
  • 技术积分: 6075
  • 用 户 组: 普通用户
  • 注册时间: 2011-09-10 00:11
个人简介

邮箱: wangcong02345@163.com

文章分类

全部博文(438)

文章存档

2017年(15)

2016年(119)

2015年(91)

2014年(62)

2013年(56)

2012年(79)

2011年(16)

分类: LINUX

2016-11-10 16:41:14

一. 总体说明
1.1 页表的初始化都是在paging_init中
paging_initsetup_arch
--> paging_init
{
    a.调用pagetable_init,建立页目录表(pgd=swapper_pg_dir=0xc0101000)
      a.1 将小于896M的页目录表与物理地址一一映射
      a.2 预留出最后32项用于高端地址映射.
         注:最后的32项中间有两项被用了,现在还不知道为什么      
    b.将cr3设为刚刚建立的页目录表基地址
    c.刷新tlb使页目录表生效
    d.kmap_init
    e.free_area_init_core中在物理地址16M=0xC1000000处,建立了mem_map用page去管理内存
     e.1 刚开始时设置page_count=0
     e.2 设置flag=reserved
     e.3 初始化zone结构体,使mem_map中virtual都指向相应的虚拟地址
    f.free_all_bootmem_core
}

二.代码分析
2.1 初始化页表
在arch/i386/mm/init.c中-->start_kernel-->setup_arch-->paging_init
  1. void __init paging_init(void)
  2. {
  3.     pagetable_init();     //2.1设置页目录表
  4. //将swapper_pg_dir=0xc0101000,赋到cr3中,告诉cpu这个是页目录表的基地址
  5.     __asm__( "movl %%ecx,%%cr3\n" ::"c"(__pa(swapper_pg_dir)));

  6. #if CONFIG_X86_PAE
  7.     /*
  8.      * We will bail out later - printk doesnt work right now so
  9.      * the user would just see a hanging kernel.
  10.      */
  11.     if (cpu_has_pae)
  12.         set_in_cr4(X86_CR4_PAE);
  13. #endif
  14. //flush一下使修改后的页目录表生效
  15.     __flush_tlb_all();

  16. #ifdef CONFIG_HIGHMEM
  17.     kmap_init();      //初始化几个变量kmap_vstart=0xffff5000,kmap_pte=0xc0003fd4,kmap_prot=0x163
  18. #endif
  19. //初始化三个管理区的size
  20.     {
  21.         unsigned long zones_size[MAX_NR_ZONES] = {0, 0, 0};     //MAX_NR_ZONES=3,DMA,NORMAL,HIGH
  22.         unsigned int max_dma, high, low;

  23.         max_dma = virt_to_phys((char *)MAX_DMA_ADDRESS) >> PAGE_SHIFT; //MAX_DMA_ADDRESS=16M,取物理地址的页帧=0x1000
  24.         low = max_low_pfn;                             //low=896M的页帧=0x38000
  25.         high = highend_pfn;                            //high=实际内存的页帧=我这儿给qemu了1G内存=0x3fffe

  26.         if (low < max_dma)
  27.             zones_size[ZONE_DMA] = low;
  28.         else {
  29.             zones_size[ZONE_DMA] = max_dma;
  30.             zones_size[ZONE_NORMAL] = low - max_dma;
  31. #ifdef CONFIG_HIGHMEM
  32.             zones_size[ZONE_HIGHMEM] = high - low;      //zones_size={0x1000, 0x37000, 0x7ffe},都是用size表示的
  33. #endif
  34.         }
  35.         free_area_init(zones_size);                //初始化各个管理区
  36.     }
  37.     return;
  38. }
2.1.1 pagetable_init
linux-2.4.18/arch/i386/mm/init.c-->paging_init-->pagetable_init
  1. static void __init pagetable_init (void)
  2. {
  3.     unsigned long vaddr, end;
  4.     pgd_t *pgd, *pgd_base;
  5.     int i, j, k;
  6.     pmd_t *pmd;
  7.     pte_t *pte, *pte_base;

  8.     //max_low_pfn=0x38000,低端内存的最高地址(最大896M)的页帧
  9.     //max_low_pfn*PAGE_SIZE=0x38000000 转为虚地址end=0xf8000000
  10.     //看出实地址转虚地址__va=实地址+0xC0000000
  11.     end = (unsigned long)__va(max_low_pfn*PAGE_SIZE);

  12.     pgd_base = swapper_pg_dir;    -->swapper_pg_dir=0xc0101000就是在kernel/head.S中的页目录表,当时只有4项
  13. #if CONFIG_X86_PAE
  14.     for (= 0; i < PTRS_PER_PGD; i++)
  15.         set_pgd(pgd_base + i, __pgd(+ __pa(empty_zero_page)));
  16. #endif
  17.     //查看虚地址0xC0000000在页目录表中处于第几项,算法:0xC0000000>>22=768
  18.     i = __pgd_offset(PAGE_OFFSET);
  19.     pgd = pgd_base + i;                -->获取内核页目录表项的地址=0xc0101000+0xc00=0xc0101c00
  20. //将ZONE_NORMAL部分的页目录表修改一下
  21.     for (; i < PTRS_PER_PGD; pgd++, i++) {    //PTRS_PER_PGD=1024
  22.         vaddr = i*PGDIR_SIZE;
  23.         if (end && (vaddr >= end))
  24.             break;
  25. #if CONFIG_X86_PAE
  26.         pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
  27.         set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
  28. #else
  29.         pmd = (pmd_t *)pgd;
  30. #endif
  31.         if (pmd != pmd_offset(pgd, 0))
  32.             BUG();
  33. //我这儿cpu_has_pse=true,所以会将页目录表项768项-992项写为0x*****1e3,一个页目录表项就映射了4M的物理地址,没有页表项了
  34.         for (= 0; j < PTRS_PER_PMD; pmd++, j++) {
  35.             vaddr = i*PGDIR_SIZE + j*PMD_SIZE;
  36.             if (end && (vaddr >= end))
  37.                 break;
  38.             if (cpu_has_pse) {                   
  39.                 unsigned long __pe;
  40.                 set_in_cr4(X86_CR4_PSE);
  41.                 boot_cpu_data.wp_works_ok = 1;
  42.                 __pe = _KERNPG_TABLE + _PAGE_PSE + __pa(vaddr);
  43.                 /* Make it "global" too if supported */
  44.                 if (cpu_has_pge) {
  45.                     set_in_cr4(X86_CR4_PGE);
  46.                     __pe += _PAGE_GLOBAL;
  47.                 }
  48.                 set_pmd(pmd, __pmd(__pe));    //写页目录表的相应项
  49.                 continue;
  50.             }
  51.             //下面这儿根本没有执行
  52.             pte_base = pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);

  53.             for (= 0; k < PTRS_PER_PTE; pte++, k++) {
  54.                 vaddr = i*PGDIR_SIZE + j*PMD_SIZE + k*PAGE_SIZE;
  55.                 if (end && (vaddr >= end))
  56.                     break;
  57.                 *pte = mk_pte_phys(__pa(vaddr), PAGE_KERNEL);
  58.             }
  59.             set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte_base)));
  60.             if (pte_base != pte_offset(pmd, 0))
  61.                 BUG();

  62.         }
  63.     }

  64.     //将ZONE_HIGHMEM部分的页目录表修改一下
  65.     //执行后vaddr=0xffc00000(物理地址1020M)(虚拟地址4092M)
  66.     //0xc0101ff0 : 0x00000000 0x00000000 0x00000000 0x00003063
  67.     vaddr = __fix_to_virt(__end_of_fixed_addresses - 1) & PMD_MASK;
  68.     fixrange_init(vaddr, 0, pgd_base);                                     -->0xc0101ffc处写入0x00003063

  69. #if CONFIG_HIGHMEM
  70.     //执行后vaddr=0xfe000000(物理地址992M),end=996M  虚地址(4064M-4068M之间)
  71.     //执行后0xc0101fe0 : 0x00004063 0x00000000 0x00000000 0x00000000
  72.     vaddr = PKMAP_BASE;
  73.     fixrange_init(vaddr, vaddr + PAGE_SIZE*LAST_PKMAP, pgd_base);          -->0xc0101fe0处写入0x00004063

  74.     pgd = swapper_pg_dir + __pgd_offset(vaddr);   //pgd=0xc0101fe0
  75.     pmd = pmd_offset(pgd, vaddr);                 //pmd=0xc0101fe0
  76.     pte = pte_offset(pmd, vaddr);                 //pte=0xc0004000
  77.     pkmap_page_table = pte;                       //pkmap_pate_talbe=0xc0004000  
  78. #endif

  79. #if CONFIG_X86_PAE
  80.     /*
  81.      * Add low memory identity-mappings - SMP needs it when
  82.      * starting up on an AP from real-mode. In the non-PAE
  83.      * case we already have these mappings through head.S.
  84.      * All user-space mappings are explicitly cleared after
  85.      * SMP startup.
  86.      */
  87.     pgd_base[0] = pgd_base[USER_PTRS_PER_PGD];
  88. #endif
  89. }
2.1.1说明 ZONE_NORMAL执行完后的页目录表项如下所示
  1. (gdb) x /1024wx 0xc0101000
  2. 0xc0101000 <swapper_pg_dir>:    0x00102027    0x00103007    0x00000000    0x00000000
  3. 0xc0101010 <swapper_pg_dir+16>:    0x00000000    0x00000000    0x00000000    0x00000000
  4. ...
  5. 0xc0101bf0 <swapper_pg_dir+3056>:    0x00000000    0x00000000    0x00000000    0x00000000   -->到此,在以上都是用户空间的页目录表
  6. 0xc0101c00 <swapper_pg_dir+3072>:    0x000001e3    0x004001e3    0x008001e3    0x00c001e3
  7. ....
  8. 0xc0101f70 <swapper_pg_dir+3952>:    0x370001e3    0x374001e3    0x378001e3    0x37c001e3   -->到此,在以上都是内核空间小于896M的页目录表
  9. 0xc0101f80 <swapper_pg_dir+3968>:    0x00000000    0x00000000    0x00000000    0x00000000
  10. 0xc0101f90 <swapper_pg_dir+3984>:    0x00000000    0x00000000    0x00000000    0x00000000
  11. 0xc0101fa0 <swapper_pg_dir+4000>:    0x00000000    0x00000000    0x00000000    0x00000000
  12. 0xc0101fb0 <swapper_pg_dir+4016>:    0x00000000    0x00000000    0x00000000    0x00000000
  13. 0xc0101fc0 <swapper_pg_dir+4032>:    0x00000000    0x00000000    0x00000000    0x00000000
  14. 0xc0101fd0 <swapper_pg_dir+4048>:    0x00000000    0x00000000    0x00000000    0x00000000
  15. 0xc0101fe0 <swapper_pg_dir+4064>:    0x00004063    0x00000000    0x00000000    0x00000000
  16. 0xc0101ff0 <swapper_pg_dir+4080>:    0x00000000    0x00000000    0x00000000    0x00003063   -->到此,留下了4*8=32项,32*4M=128M可能跟highmem有关,以后再补充
  17. 因为打开了ps位,所以cpu是按照一级映射来的,所以页目录表中存的直接就是物理地址,因为4M=0x400000,所以都是按4M累加的0x370001e3-->0x374001e3
  18. 其中0x00003063 是__end_of_fixed_addresses
  19.    0x00004063  是PKMAP_BASE的映射
用户空间的PTE个数=768项(0xc0101000 --0xc0101c00)-->768项PTE, 每个PTE映射4M内存,则768项PTE可以映射768*4M=3072M=3G内存
内核空间的PTE个数=224项(0xc0101c00 -- 0xc0101f80)-->224项PTE, 每个PTE映射4M内存,则224项PTE可以映射224*4M=896M的内存
PTE=0x1E3=  111100011  =  1 1110 0011 -->  PS=1  -->如果PS=1,这个页目录表项就指向4MB的物理页面,而不是像原来那样指向一个页表

高端地址=剩下256-224=32个PTE,32个PTE可以映射128M内存

2.1.2 高端地址页目录表的修改
//start=0xffc00000(物理地址1020M), end=0x0, pgd_base=0xc0101000
  1. static void __init fixrange_init (unsigned long start, unsigned long end, pgd_t *pgd_base)
  2. {
  3.     pgd_t *pgd;
  4.     pmd_t *pmd;
  5.     pte_t *pte;
  6.     int i, j;
  7.     unsigned long vaddr;

  8.     vaddr = start;
  9.     i = __pgd_offset(vaddr);        //start在页目录表中的第几项,vaddr=0xffc00000,执行后i=0x3ff
  10.     j = __pmd_offset(vaddr);        //这个有数据,但是假的,下面没有
  11.     pgd = pgd_base + i;             //start在页目录表中的偏移。执行后pgd=0xc0101ffc=0xc0101000+0x3ff*4

  12.     for ( ; (i < PTRS_PER_PGD) && (vaddr != end); pgd++, i++) {
  13. #if CONFIG_X86_PAE
  14.         if (pgd_none(*pgd)) {
  15.             pmd = (pmd_t *) alloc_bootmem_low_pages(PAGE_SIZE);
  16.             set_pgd(pgd, __pgd(__pa(pmd) + 0x1));
  17.             if (pmd != pmd_offset(pgd, 0))
  18.                 printk("PAE BUG #02!\n");
  19.         }
  20.         pmd = pmd_offset(pgd, vaddr);
  21. #else
  22.         pmd = (pmd_t *)pgd;                //直接将pmd=pgd了,pmd不影响真正的页目录表的内容,只是逻辑上有用
  23. #endif
  24.         for (; (j < PTRS_PER_PMD) && (vaddr != end); pmd++, j++) {
  25.             if (pmd_none(*pmd)) {
  26.                 pte = (pte_t *) alloc_bootmem_low_pages(PAGE_SIZE);    //内存管理区中是0x7-->0xF,对应的物理地址=0x00003000
  27.                 set_pmd(pmd, __pmd(_KERNPG_TABLE + __pa(pte)));        //将start在页目录表项中的内容改为0x00003063
  28.                 if (pte != pte_offset(pmd, 0))
  29.                     BUG();
  30.             }
  31.             vaddr += PMD_SIZE;
  32.         }
  33.         j = 0;
  34.     }
  35. }
0x00003063 -->页表的物理地址0x3000 -->4(DIRTY) 2(ACCESSED)   2(RW)  1(PRESENT)


附录1.关于PMD

《深入理解Linux虚拟内存管理》
a. 
include/asm/pgtable-2level.h中
  1. static inline pmd_t * pmd_offset(pgd_t * dir, unsigned long address)
  2. {
  3.     return (pmd_t *) dir;
  4. }
从这里可以看出"PMD页面帧偏移量“根本就不存在,而不是0

b. 在include/asm/pgtable-3level.h中
  1. #define pmd_offset(dir, address) ((pmd_t *) pgd_page(*(dir)) + __pmd_offset(address))
  2.             
  3. #define pgd_page(pgd) ((unsigned long) __va(pgd_val(pgd) & PAGE_MASK))

  4. #define __pmd_offset(address) (((address) >> PMD_SHIFT) & (PTRS_PER_PMD-1))



kernel/head.S中只留了一个页目录表
虚拟地址0xC0000000在页目录表的第768项上,一个页目录表1024项
1024-768=256
256*4M=1024M  -->所以内核用的虚地址只有1G -->0xC0000000-0xFFFFFFF

上图出自《64-ia-32-architectures-software-developer-manual.pdf》Vol3_ch4 Paging


阅读(1260) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~