内核临时页表建立

背景:为什么要建立内核临时页表:当内核被解压到线性地址0x100000后,为了继续启动内核,即启动内核的第一进程即swapper进程,内核需要建立一张临时页表供其使用。
 
当内核从16位的实模式进入到保护模式(通过在汇编代码中的setup函数中设置linux的cr0寄存器的PE位),内核要创建一个有限的地址空 间,容纳内核的代码段、数据段、初始页表和用于存放动态数据结构的共128KB大小的空间,此时通常的程序设计者假定,内核使用的段、临时页表和 128KB的内存范围可以全部放在RAM前8MB的空间内。于是我们需要做的工作是建立一个页表可以对内存的前8MB的物理地址进行寻址。
 
由于进程的线性地址空间分成两部分:
0x00000000到0xbfffffff(0-3G)的线性地址,无论进程运行在用户态还是内核态都可以寻址。
0xc0000000到0xffffffff(3G-4GB)的线性地址,只有内核态进程才可以寻址。
 
为了保证在实模式和保护模式下都可以很容易的对这8MB寻址。因此,内核必须建立两个映射。把从0x00000000到0x007fffff的线性 地址和从0xc0000000到0xc07fffff的线性地址都映射到从0x00000000到0x007fffff的物理地址中。
此时可以通过与物理地址相同的线性地址或者通过从0xc0000000开始的8MB线性地址对RAM的前8MB进行寻址。
 
开始建立内核临时页表
内核的临时页表是通过arch/i386/kernel/head.s汇编代码中的startup_32()函数实现的。
建立一个完整的二级页表,需要建立一个页全局目录和一个页表。由于只需要映射8MB的地址空间,一个页表可以容纳1024项,每个页大小为4k,8MB=2*1024*4k;所以只需要2个页全局目录项和一张页表即可。
 
页全局目录项的建立:
页全局目录项有1024项,但是我们每次寻址8MB的空间只需要2个页全局目录项即可,由于我们要同时考虑用户态和内核态的寻址所以我们需要4个页全局目录项分别寻址8MB的用户空间和8MB的内核空间。
要建立页全局目录我们首先要知道页全局目录存放的物理地址,而变量swapper_pg_dir 存放了页全局目录的线性地址
(swapper_pg_dir的线性地址可以在/boot/System.map文件中找到)通过执行swapper_pg_dir - __PAGE_OFFSET计算可以获得swapper_pg_dir 的物理地址(其中__PAGE_OFFSET为0xc0000000是内核线性空间的起始地址)。
知道了临时页全局目录的地址之后,要做的便是初始化临时页全局目录:
页全局目录的初步初始化:
417 ENTRY(swapper_pg_dir)
418     .fill 1024,4,0
这两行汇编代码执行了页全局目录的初步初始化,它的意思是从swapper_pg_dir开始,填充1024项,每项为4字节,值为0,正好是4K一个页面。
页全局目录进一步初始化:
为映射8MB地址空间,内核通过填充swapper_pg_dir中第0项,1项,768项和769项实现(768和769是通过计算内核线性地址 空间对应的页目录偏移量获得的)。前两项是给用户线性地址映射,后两项给内核线性地址映射。内核会将swapper_gp_dir的0项和768项字段设 置为pg0的物理地址(pg0中存放第一张页表的地址),而1项和769项设置为紧随pg0后的页框的物理地址(一般是pg0+4k)。


页表的建立
由于物理地址8MB=2*1024*4k,可以对应2048个页框,建立两个页表的话只需从0x00000000开始,以此将每隔0x1000的地址填入页表中的每一项中即可:0x0000,0x1000,0x2000,…0x3ff00
0x40000,0x41000,…0x7ff000
 
 
内核中有变量pg0,表示对应的页表。建立页表的过程如下:
091 page_pde_offset = (__PAGE_OFFSET >> 20);
092
093  movl $(pg0 - __PAGE_OFFSET), %edi
094  movl $(swapper_pg_dir - __PAGE_OFFSET), %edx
095  movl $0x007, %eax
096 10:
097 leal 0x007(%edi),%ecx
098 movl %ecx,(%edx)
099 movl %ecx,page_pde_offset(%edx)
100 addl $4,%edx
101 movl $1024, %ecx
102 11:
103 stosl
104         addl $0x1000,%eax
105         loop 11b
106        
107        
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)
大致意思是从0开始,把连续的线性地址映射到物理地址。0x007正好表示PRESENT+RW+USER(在内存中,可读写,用户页面,这样在用 户态和内核 态都可读写)。由于每个页表项有32位,但其实只需保存物理地址的高20位 就够了,所以剩下的低12位可以用来表示页的属性。
结束条件:从代码中可知,当映射到当前所操作的页表项往下INIT_MAP_BEYOND_END(128K)处映射结束。

建成的页表示意图如下

 

这样总的页表建立之后的示意图如下


开启页面映射之后就可以引用内核的变量,但是还不能启动start_kernel,要启动swapper进程还需要设置内核堆栈
193        
194         lss stack_start,%esp
然后设置中断向量表
215         call setup_idt
检查CPU类型
载入gdt(原来的gdt是临时的)和ldt
302         lgdt cpu_gdt_descr
303         lidt idt_descr
最后,调用start_kernel
327         call start_kernel
至此启动start_kernel函数
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值