page_pde_offset = (__PAGE_OFFSET >> 20); /* __PAGE_OFFSET是0xc0000000,page_pde_offset = 3072 = 0xc00,是页目录中的偏移 */ 36 37 movl $(pg0 - __PAGE_OFFSET), %edi /* 将pg0对应的物理地址送到edi */ 38 movl $(swapper_pg_dir - __PAGE_OFFSET), %edx /* 将swapper_pg_dir(存放临时页全局目录的地址)送到edx */ 39 movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */ 40 10: 41 leal 0x007(%edi),%ecx /* Create PDE entry —— 构造一个页目录项(地址+标志位),把edi指向的物理地址加上0x007放入ecx中 */ 42 movl %ecx,(%edx) /* 第一次循环时把ecx中的内容放入swapper_pg_dir的第0项里,第二次循环时把ecx中的内容放入swapper_pg_dir的第1项里 */ 43 movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */ 44 addl $4,%edx /* edx加上一个页目录项长度(4字节),指向页全局目录的下一个页目录项地址 */ 45 movl $1024, %ecx /* 初始化1024个页目录项,设置计数器 */ 46 11: 47 stosl /* eax -> [edi]; edi = edi + 4 */ 48 addl $0x1000,%eax /* 更改eax的值,为下次stosl作准备 */ 49 loop 11b /* 循环操作,其实就是初始化1024个页表项 */ 50 /* End condition: we must map up to and including INIT_MAP_BEYOND_END */ 51 /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */ 52 leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp /* ebp = edi指向的物理地址 + INIT_MAP_BEYOND_END(128K)+ 0x007 */ 53 cmpl %ebp,%eax /* 比较ebp与eax */ 54 jb 10b /* 如果eax < ebp,则跳到上面10的地方 */ 55 movl %edi,(init_pg_tables_end - __PAGE_OFFSET) /* 此时的edi中存放pg0+0x2000,将此值存入init_pg_tables_end中,表示页表初始化结束 */ 56
下面的是较详细的解释(结合我的作业题目):
初始化临时内核页表是在startup_32汇编语言函数中完成的。在ULK所述中,假设内核能容纳于RAM的前8MB空间,然后对RAM的前8MB进行恒等映射
(例如用户地址0x00003000映射物理地址0x00003000,0xc0003000映射到物理地址0x00003000),来初始化临时页全局目录swapper_pg_dir和相应的页表。
映射8MB只需要填充swapper_pg_dir中第0项,1项,768项和769项。前两项是给用户线性地址映射,后两项给内核线性地址映射。用页全局目录里的两项就能对8MB映射的
理由是2×1024(页表有1024项)×4K(一页的大小)=8M。实际上初始化内核页表来对RAM的前8MB映射不是个硬性的规定。
这取决于你的内核的配置(我认为大多数情况下是对8MB映射)。在startup_32中可以看到,对多少内存进行映射是通过pg0动态判断的。 linux/arch/i386/kernel/head.S page_pde_offset = (__PAGE_OFFSET >> 20); /*__PAGE_OFFSET是0xc0000000,内核线性空间的起始地址。 page_pde_offset=0xc00(十进制为3072)*/ movl $(pg0 - __PAGE_OFFSET), %edi /*假设pg0的物理地址(00567000),放入edi中。*/ movl $(swapper_pg_dir - __PAGE_OFFSET), %edx /*swapper_pg_dir的物理地址,放入edx中。*/ movl $PTE-ATTR, %eax /* PTE-ATTR=0x007 页目录项和页表项的低12位是标志位,把标志位0x007放入eax中。*/ 10: leal PDE-ATTR(%edi),%ecx /* Create PDE entry */ /*第一循环时把edi指向的pg0的物理地址加上PDE-ATTR放入ecx中。 第二次循环时把edi指向的物理地址0x567000加上PDE-ATTR放入ecx中。
linux2.6/arch/x86/kernel/head_32.S 中的PDE-ATTR=0x067
*/ movl %ecx,(%edx) /* Store identity PDE entry */ /*第一次循环时把ecx中的内容放入swapper_pg_dir的第0项里。 第二次循环时把ecx中的内容放入swapper_pg_dir的第1项里。*/ movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */ /*第一次循环时把ecx中的内容放入swapper_pg_dir的第768项里。因为前面算出page_pde_offset的值为3072,而swapper_pg_dir中每项是4个字节,所以3072/4=768。 第二次循环时把ecx中的内容放入swapper_pg_dir的第769项里。*/ addl $4,%edx /*第一次循环时,此时edx指向swapper_pg_dir的第1项。 第二次循环时,此时edx指向swapper_pg_dir的第2项。*/ movl $1024, %ecx /*为初始化1024个页表项设置计数*/ 11: stosl /*把eax中的内容放入edi指向的物理地址中,然后edi+4。*/ addl $0x1000,%eax loop 11b /*跳到上面的11处循环。 第一次执行1024次后,从pg0物理地址(0x567000)开始存放的是0x007,0x1007,0x2007,...,0x3ff007,也就是当前能够映射到物理地址从0x000到0x3fffff处。此时edi中的值为0x567000。 第二次执行1024次后,从物理地址(0x568000)开始存放的是0x400007,0x401007,0x402007,...,7ff007,也就是当前能够映射到物理地址0x000到7fffff处,正好8MB。此时edi中的值为0x568000。*/ leal (INIT_MAP_BEYOND_END+PTE-ATTR)(%edi),%ebp /*INIT_MAP_BEYOND_END的值为128k,在此文件中的一个宏定义。把edi指向的物理地址加上128k加上0x007放入edp中。*/ cmpl %ebp,%eax /*在第一次循环中ebp中的值为0x567007,eax中的值为0x400007小于0x515007。当前所映射到的最大物理地址为0x3fffff没有包含0x567007,所以没有映射完。 在第二次循环中ebp中的值为0x568007,eax中的值为0x800007大于0x568007。当前所映射到的最大物理地址为0x7fffff包含了0x568007,所以物理地址映射完毕。*/ jb 10b /*第一次循环做完时跳到上面的10处继续循环 第二次循环做完时跳出循环。*/
题目:
综上分析:
1):填写在swapper_pg_dir中的第0项的内容是:0x567067
2):填写在swapper_pg_dir中的第1项的内容是:0x568067
3):填写在pg0的第0项是0x007
填写在pg0的第1项是0x1007
填写在pg0的第0x3FF项是0x3FF007
4):0-4M-1的空间对应的物理地址空间为:0x000-0x3fffff
5):swapper_gd_dir的第768项和769项的内容分别为:0x567067和0x568067
和第0项和第1项是相同的。因为第0,1项是给用户线性地址映射的。而前者是为内核线性地址映射的。
中间有什么错误,望同学指出!