启用分页机制

Linux 启动时,首先运行在实模式下,随后就要转到保护模式下运行。因为在第二章段机制 中,我们已经介绍了 Linux 对段的设置,在此我们主要讨论与分页机制 相关的问题。 Linux 内核代码的入口点就是 /arch/i386/kernel/head.S 中的 startup_32

1 .页表的初步初始化:

/*
* The page tables are initialized to only 8MB here - the final page
* tables are set up later depending on memory size.
*/

. org 0x2000
ENTRY( pg0)
 
. org 0x3000
ENTRY( pg1)
 
/*
* empty_zero_page must immediately follow the page tables ! (The
* initialization loop counts until empty_zero_page)
*/

 
. org 0x4000
ENTRY( empty_zero_page)
 
 /*
 * Initialize page tables
 */

 
         movl $pg0- __PAGE_OFFSET, % edi /* initialize page tables */
         movl $007, % eax /* "007" doesn't mean with right to kill, but
                                   PRESENT+RW+USER */

 2: stosl
         add $0x1000, % eax
         cmp $empty_zero_page- __PAGE_OFFSET, % edi
         jne 2b

   
内核的这段代码执行时,因为页机制 还没有启用,还没有进入保护模式,因此指令寄存器 EIP 中的地址还是物理地址,但因为 pg0 中存放的是虚拟地址 (想想 gcc 编译内核以后形成的符号地址都是虚拟地址 ),因此,“ $pg0-__PAGE_OFFSET ”获得 pg0 的物理地址,可见 pg0 存放在相对于内核代码起点为 0x2000 的地方 , 即物理地址为 0x00102000 ,而 pg1 的物理地址则为 0x00103000 Pg0 pg1 这个两个页表中的表项则依次被设置为 0x007 0x1007 0x2007 等。其中最低的三位均为 1 ,表示这两个页为用户页,可写,且页的 内容在内存中(参见图 2.24 )。所映射的物理页的基地址则为 0x0 0x1000 0x2000 等,也就是物理内存中的页面 0 1 2 3 等等,共映射 2K 页面,即 8MB 的存储空间。由此可以看出, Linux 内核对物理内存的最低要求为 8MB 。紧接着存放的是 empty_zero_page ( 即零页 ) ,零页存放的是系统启动参数和命令行参数

2 .启用分页机制

/*
  * This is initialized to create an identity- mapping at 0- 8M ( for bootup
  * purposes) and another mapping of the 0- 8M area at virtual address
  * PAGE_OFFSET.
  * /


. org 0x1000
ENTRY( swapper_pg_dir)
         . long 0x00102007
         . long 0x00103007
         . fill BOOT_USER_PGD_PTRS- 2, 4, 0
         /* default: 766 entries * /
         . long 0x00102007
         . long 0x00103007
         /* default: 254 entries * /
        . fill BOOT_KERNEL_PGD_PTRS- 2, 4, 0

/*
 * Enable paging
 * /
3:
       movl $swapper_pg_dir- __PAGE_OFFSET, % eax
       movl %eax , % cr3 /* set the page table pointer. . * /
       movl %cr0, % eax
       orl $0x80000000, % eax
       movl %eax , % cr0 /* . . and set paging ( PG) bit * /
       jmp 1f /* flush the prefetch- queue * /
1:
       movl $1f, % eax
       jmp * % eax /* make sure eip is relocated * /
1:

 

我们先来看这段代码的功能。这段代码就是把页目录 swapper_pg_dir 的物理地址装入控制寄存器 cr3 ,并把 cr0 中的最高位置成 1 ,这就开启了分页机制。

但是,启用了分页机制,并不说明 Linux 内核真正进入了保护模式,因为此时,指令寄存器 EIP 中的地址还是物理地址,而不是虚地址。“ jmp 1f ”指令从逻辑上说不起什么作用,但是,从功能上说它起到丢弃指令流水线中内容的作用(这是 Intel i386 技术资料中所建议的),因为这是一个短跳转, EIP 中还是物理地址。紧接着的 mov jmp 指令把第二个标号为 1 的地址装入 EAX 寄存器并跳转到那儿。在这两条指令执行的过程中 , EIP 还是指向物理地址“ 1MB +某处”。因为编译程序使所有的符号地址都在虚拟内存空间中,因此,第二个标号 1 的地址就在虚拟内存空间的某处( (PAGE_OFFSET+ 某处 ) ,于是, jmp 指令执行以后, EIP 就指向虚拟内核空间的某个地址,这就使 CPU 转入了内核空间,从而完成了从实模式到保护模式的平稳过渡。

说明:这里可以推测: movl $1f ,%eax中的$1f是一个符号地址,编译后在二进制文件中看到的地址为 PAGE_OFFSET+某个偏移.jmp 1f,这个1f就不是符号地址了,有可能只是个相对地址。

然后再看页目录 swapper_pg_dir 中的内容。从前面的讨论我们知道 pg0 pg1 这两个页表的起始物理地址分别为 0x00102000 0x00103000 。从图 2­.22 可知,页目录项的最低 12 位用来描述页表的属性。因此,在 swapper_pg_dir 中的第 0 和第 1 个目录项 0x00102007 0x00103007 ,就表示 pg0 pg1 这两个页表是用户页表、可写且页表的内容在内存。

接着,把 swapper_pg_dir 中的第 2 767 766 个目录项全部置 0 。因为一个页表的大小为 4KB ,每个表项占4 字节,即每个页表含有 1024 个表项,每个页的大小也为 4KB ,因此这 768 个目录项所映射的虚拟空间为 768 ´ 1024 ´ 4K 3G ,也就是 swapper_pg_dir 表中的前 768 个目录项映射的是用户空间。

最后,在第 768 769 个目录项中又存放 pg0 pg1 这两个页表的地址和属性,而把第 770 1023 254 目录项置 0 。这 256 个目录项所映射的虚拟地址空间为 256 ´ 1024 ´ 4K 1G ,也就是 swapper_pg_dir 表中的后 256 个目录项映射的是内核空间。



6.6 初始页目录 swapper_pg_dir 的映射图

由此可以看出,在初始的页目录 swapper_pg_dir 中,用户空间和内核空间都只映射了开头的两个目录项,即 8MB 的空间,而且有着相同的映射,如图 6.6 所示。

说明:要理解这个图,如我们寻址虚拟地址3G,虚拟地址是:0xc0000000,页目录索引为1100000000=768,由此找到页表地址:0x00102007(页对齐),就是上一节代码中:

......

.org 0x2000

......

处定义的那个页表,页表索引为虚拟地址中间10位,为0,即该页表中的第一个表项,其值为: 0x007 ,就是第一个物理页帧,物理地址为0。

     读者会问,内核开始运行后运行在内核空间,那么,为什么把用户空间的低区( 8M )也进行映射,而且与内核空间低区的映射相同?简而言之,是为了从实模式到保护模式的平稳过渡。具体地说,当 CPU 进入内核代码的起点 startup_32 后,是以物理地址来取指令的。在这种情况下,如果页目录只映射内核空间,而 映射用户空间的低区,则一旦开启页映射机制以后就不能继续执行了,这是因为,此时 CPU 中的指令寄存器 EIP 指向低 区,仍会以物理地址取指令,直到以某个符号地址为目标作绝对转移或调用子程序为止。所以, Linux 内核就采取了上述的解决办法。    

但是,在 CPU 转入内核空间以后,应该把用户空间低区的映射清除掉。后面读者将会看到,页目录 swapper_pg_dir 经扩充后就成为所有内核线程的页目录。在内核线程的正常运行中,处于内核态的 CPU 是不应该通过用户空间的虚拟地址访问内存的。清除了低区的映射以后,如果发生 CPU 在内核中通过用户空间的虚拟地址访问内存,就可以因为产生页面异常而捕获这个错误。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值