Linux系统学习之内存初始化

接下来的一段时间准备系统学习一下Linux的源代码,能顺利回答下列问题,Linux的i386内存寻址部分就算过关了。(题目出自陈香兰老师授课讲稿)

在32位pc中,结合Linux2.6.26/arch/x86/kernel/head_32.S中228-251行相关代码,关于临时2级页表的初始化过程,假设pg0所在的物理地址是0x567000,回答下列问题(以下涉及到数值的地方,请用16进制表示):
填写在swapper_pg_dir中第0x0项的内容是什么,有什么含义?
若填写了swapper_pg_dir中第0x1项,则此内容是什么?
填写在pg0的第0x0项、第0x1项和第0x3FF项的内容是多少,有什么含义?
根据swapper_pg_dir的第0x0项和pg0的内容,这个临时页表所代表的地址空间中,0~4MB-1的空间被映射的物理地址空间范围是什么?
若内核地址空间从3G开始,那么填写在swapper_pd_dir中第0x300项和0x301项的内容是什么,与上述第0项和第1项有什么关系,有什么含义?

唠叨两句,线性地址和物理地址经过硬件转化,所以看到奇奇怪怪的物理地址也不用担心,这跟逻辑地址没什么直接联系,另外,读代码之前要先明白这段代码是做swapper_pg_dir初始化用的,带着目的读会提高效率. 回顾x86的内存寻址过程,大概是CR3寄存器配合PDT,然后找到PTE的某项后加Page Offset找到所要的内容。不过这里是临时页表的初始化,NO PAE,OK,继续!

/arch/x86/kernel/head_32.S 页目录初始化代码节选
/* Physical address */
#define pa(X) ((X) – __PAGE_OFFSET)
page_pde_offset = (__PAGE_OFFSET >> 20);
/*__PAGE_OFFSET是0xc0000000,低于这个地址的被划分为用户空间,因此page_pde_offset是0xc00,页目录偏移,也就是整个页目录的大小,看不懂没关系,稍后会用到*/
  • movl $pa(__brk_base), %edi
    /*__brk_base由内核编译时指定,表示初始时堆的开始地址,注意kernel将所有的初始化好的页表都放在堆的开始处,这里所说的地址都是指运行时的虚拟地址VA,具体这里应该是pg0对应的物理地址,存入edi,也就是第一个page所在的地址,swapper_pg_dir的第一项*/
  • movl $pa(initial_page_table), %edx
    /*initial_page_table在paging_init()初始化,将swapper_pg_dir(存放页全局目录PGD的地址)送入edx*/
  • movl $PTE_IDENT_ATTR, %eax
    /*
    x86/include/asm/pgtable_types.h(#define PTE_IDENT_ATTR 0x003) PTE的属性是0x003,即PRESENT+RW,另外还有 #define PDE_IDENT_ATTR   0x067      /* PRESENT+RW+USER+DIRTY+ACCESSED */  #define PGD_IDENT_ATTR   0x001      /* PRESENT (no other attributes) */
    */

10:

  • leal PDE_IDENT_ATTR(%edi),%ecx /* Create PDE entry */
    补充说明:leal(加载有效地址)是取寄存器的地址赋值到右边,
    leal     S,D 结果:&S->D
    movel S,D 结果:S->D
    movel (S), D 结果:&S->D
    leal (S), D 结果:S->D
    ecx=edi+PDE_INDENT_ATTR
    /*把edi的值即pg0所在物理地址加0x067(PDE的属性)放入ecx,构建一个页目录项*/
    /*第二次循环,edi寄存器指向pg0+1024×4,即pg1的物理地址不用再赋值,下同*/
  • movl %ecx,(%edx) /* Store identity PDE entry */
    /*然后把ecx(pg0及属性)送入swapper_pg_dir的第零项,注意edx存放的是swapper_pg_dir的物理地址,因此该行代码把pg0及属性写入swapper_pg_dir第一项*/
  • movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
    /*再把ecx送到swapper_pg_dir的第768项,page_pde_offset为0xc00,swapper_pg_dir每项为4字节,因此为768项,回顾1024个页表项,前768为用户空间,后256为内核空间,这里叫kernel PDE entry是这个意思*/
    /*第二轮循环,edx已经自加了4,下同*/
  • addl $4,%edx
    /*本段代码循环执行,edx的增量为4,即swapper_pg_dir每项的大小,先往后看*/
  • movl $1024, %ecx
    /*准备初始化1024个表项,这个是和loop配合使用的,记得否?*/
    /*tag 10做的工作就是创建一个PDE entry并放入ecx,然后加两个属性标志位存在swapper_pg_dir里*/
11:
  • stosl/*eax(0x003)的内容放入edi指向的物理地址(pg0),然后edi+4,*/
  • addl $0x1000,%eax
    /*eax: 0x1003, 0x2003, 0x3003 …, 0x3ff003*/
    /*提示:pg0页有4K,按照edi+4依次递增放入eax的值,换句话说,pg0的第零项是0x0003,第一项0x1003…, 第1023项,0x3ff003*/
    /*第二轮循环,eax每轮循环增长4K*1024=4M,最终会超过ebp的值,对应swapp_pg_dir每个PDE,下同*/
  • loop 11b
    /*循环执行前两行代码,初始化了pg0的全部1024个页表项,每个4B,这个时候,swapper_pg_dir的第一项,pg0已经全部初始化完毕(真累),再补充说明下,这个pg0其实就对应了物理地址前4MB了。
  • /*
  • * End condition: we must map up to the end + MAPPING_BEYOND_END.
  • */
  • movl $pa(_end) + MAPPING_BEYOND_END + PTE_IDENT_ATTR, %ebp
    /*ebp指向指向的物理地址+MAPPING_BEYOND_END+0x003*/
    /*需要映射到end+MAPPING_BEYOND_END,将内核最终地址保存到这里,当eax<ebp则重复初始化过程,ebp推算过程如下:

    • Enough space to fit pagetables for the low memory linear map */
    • #define PAGE_TABLE_SIZE(pages) ((pages) / PTRS_PER_PGD)
      #define PTRS_PER_PGD 1024
      #define PAGE_SHIFT 12
      PAGE_TABLE_SIZE(0X40000000>>12) <<12
    • MAPPING_BEYOND_END = \
      PAGE_TABLE_SIZE(((1<<32) – __PAGE_OFFSET) >> PAGE_SHIFT)  <<  \ PAGE_SHIFT
    • PAGE_OFFSET 以外的地址包括0x100000000-0xc0000000=0x40000000,共0x40000页,算得配套页表大小 PAGE_TABLE_SIZE为0x40000/1024=0x100(256个) MAPPING_BEYOND_END为0x100>>PAGE_SHIFT = 0x100000,即256KB
  • cmpl %ebp,%eax
    /*eax=0x3ff03*/
    /*第二轮 eax = 0x7ff03 …. */
  • jb 10b
    /*至此,执行完毕,swapper_pg_dir所指向的PDE已初始化完毕,*/
  • addl $__PAGE_OFFSET, %edi
  • movl %edi, pa(_brk_end)
  • shrl $12, %eax
  • movl %eax, pa(max_pfn_mapped)
  • /* Do early initialization of the fixmap area */
  • movl $pa(initial_pg_fixmap)+PDE_IDENT_ATTR,%eax
  • movl %eax,pa(initial_page_table+0xffc)
至此,回到刚才的问题:
填写在swapper_pg_dir中第0x0项的内容是什么,有什么含义?
swapper_pg_dir第0项内容是pg0的物理地址+PDE_INDENT_ATTR,也就是0x567067, 表示该页表是用户页表,可读写,可访问..(PRESENT+RW+USER+DIRTY+ACCESSED)
若填写了swapper_pg_dir中第0x1项,则此内容是什么?
0x568067
填写在pg0的第0x0项、第0x1项和第0x3FF项的内容是多少,有什么含义?
(回想stosl,即 movl eax, edi; addl $4, edi)pg0的第0项表示第一页的物理地址,内容是0x003, 第二页物理地址是0x1003 … 下同. 它们对应物理地址前4MB.
根据swapper_pg_dir的第0x0项和pg0的内容,这个临时页表所代表的地址空间中,0~4MB-1的空间被映射的物理地址空间范围是什么?
显然是物理地址0~4MB-1
若内核地址空间从3G开始,那么填写在swapper_pd_dir中第0x300项和0x301项的内容是什么,与上述第0项和第1项有什么关系,有什么含义?
第0x300项(768项)与0x301项(769项)分别和0x000项与0x001相同,分别为0x567067和0x568067,这是为了实模式到保护模式的平稳过度,在启用分页后可以继续执行.
swapper_pg_dir包含的1024个页目录项,前768指向用户空间,后256指向内核空间