浅析linux内核内存管理之临时内核页表
91 计算__PAGE_OFFSET在页目录中的偏移
93 %edi保存pg0的物理地址,pg0定义在连接脚本 arch/i386/kernel/vmlinux.lds中,gcc编译内核以后形成的符号地址都是虚拟地址,在我的系统redhat 9.0内核2.6.11环境下,pg0为 0xc04d4000,这个可以在System.map文件中查看到
94 将swapper_pg_dir的物理地址存放到%edi中
95 存放低12位的标志到%eax中
97 将pg0的物理地址与标志位合到一起,创建一个页目录项
98 将这个页目录项存储在swapper_pg_dir的临时页全局目录中,在第一次循环中填充0号表项,第二次循环填充1号表项
99 在第一次循环填充768号表项,第二次循环填充769号表项
100 edx指向下一个表项
101 设置计数,即要设置临时页全局目录表1024个表项
103 eax->[edi];edi = edi + 4
104 填充pg0时,一个表项对应一个page,一个page是4KB,所以加0x1000
105 一直循环,填充pg0的1024个表项;下一次大循环,填充pg0后边的那个页表的1024个表项
108 将INIT_MAP_BEYOND_END+0x007+%edi与%ebp比较,如果小于就进行下一轮大循环,此时是填充pg0后的第二个页表
111 init_pg_tables_end存放pg0+0x2000
获得临时页全局目录的物理地址,存放在cr3中,并设置cr0的PG标志位,此时开启了分页功能
临时页全局目录是在内核编译过程中静态地初始化的,而临时页表是由startup_32()汇编语言函数初始化的。临时页全局目录存放在swapper_pg_dir变量中,临时页表在pg0变量开始出存放,紧接在内核未初始化的数据段(_end符号后)后面。假设内存使用的段,临时页表和128KB的内存范围能容纳于RAM前8MB空间,为了映射8MB空间需要两个页表,一个是pg0处,另一个存放在pg0后边。为了在实模式和保护模式下都能对这8MB寻址,因此内核必须创建一个映射,把从0x00000000到0x007fffff的线性地址和从0xc0000000到0xc07fffff的线性地址映射到从0x00000000到0x007fffff。所以要设置临时页全局目录的第0,1,768,769项,其他项为0。
临时内核页表设置的代码在arch/i386/kernel/head.S中:
- 91page_pde_offset = (__PAGE_OFFSET >> 20);
- 92
- 93 movl $(pg0 - __PAGE_OFFSET), %edi
- 94 movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
- 95 movl $0x007, %eax /* 0x007 = PRESENT+RW+USER */
- 9610:
- 97 leal 0x007(%edi),%ecx /* Create PDE entry */
- 98 movl %ecx,(%edx) /* Store identity PDE entry */
- 99 movl %ecx,page_pde_offset(%edx) /* Store kernel PDE entry */
- 100 addl $4,%edx
- 101 movl $1024, %ecx
- 10211:
- 103 stosl
- 104 addl $0x1000,%eax
- 105 loop 11b
- 106 /* End condition: we must map up to and including INIT_MAP_BEYOND_END */
- 107 /* bytes beyond the end of our own page tables; the +0x007 is the attribute bits */
- 108 leal (INIT_MAP_BEYOND_END+0x007)(%edi),%ebp
- 109 cmpl %ebp,%eax
- 110 jb 10b
- 111 movl %edi,(init_pg_tables_end - __PAGE_OFFSET)
93 %edi保存pg0的物理地址,pg0定义在连接脚本 arch/i386/kernel/vmlinux.lds中,gcc编译内核以后形成的符号地址都是虚拟地址,在我的系统redhat 9.0内核2.6.11环境下,pg0为 0xc04d4000,这个可以在System.map文件中查看到
94 将swapper_pg_dir的物理地址存放到%edi中
95 存放低12位的标志到%eax中
97 将pg0的物理地址与标志位合到一起,创建一个页目录项
98 将这个页目录项存储在swapper_pg_dir的临时页全局目录中,在第一次循环中填充0号表项,第二次循环填充1号表项
99 在第一次循环填充768号表项,第二次循环填充769号表项
100 edx指向下一个表项
101 设置计数,即要设置临时页全局目录表1024个表项
103 eax->[edi];edi = edi + 4
104 填充pg0时,一个表项对应一个page,一个page是4KB,所以加0x1000
105 一直循环,填充pg0的1024个表项;下一次大循环,填充pg0后边的那个页表的1024个表项
108 将INIT_MAP_BEYOND_END+0x007+%edi与%ebp比较,如果小于就进行下一轮大循环,此时是填充pg0后的第二个页表
111 init_pg_tables_end存放pg0+0x2000
- 183/*
- 184 * Enable paging
- 185 */
- 186 movl $swapper_pg_dir-__PAGE_OFFSET,%eax
- 187 movl %eax,%cr3 /* set the page table pointer.. */
- 188 movl %cr0,%eax
- 189 orl $0x80000000,%eax
- 190 movl %eax,%cr0 /* ..and set paging (PG) bit */
- 191 ljmp $__BOOT_CS,$1f /* Clear prefetch and normalize %eip */
- 1921:
- 193 /* Set up the stack pointer */
- 194 lss stack_start,%esp
获得临时页全局目录的物理地址,存放在cr3中,并设置cr0的PG标志位,此时开启了分页功能