高端内存映射

1 内核空间和用户空间
 

用户空间:在Linux中,每个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB的虚存地址是用户空间,通过每个进程自己的页目录、页表,用户进程可以直接访问。


内核空间:从3GB到4GB的虚存地址为内核态空间,存放供内核访问的代码和数据,用户态进程不能访问,只有内核态进程才能寻址。所有进程从3GB到4GB的虚拟空间都是一样的,linux以此方式让内核态进程共享代码段和数据段。

 

由于虚拟机制的引入,进程的可以使用32位地址系统支持的全部4G线性空间。进程的线性地址空间分成两部分:
• 从0x00000000 到 0xbfffffff的线性地址,无论用户态还是内核态的进程都可以寻址。
• 从0xc0000000 到 0xffffffff的线性地址,只有内核态的进程才能寻址。


当进程运行在用户态时,它产生的线性地址小于0xc0000000;当进程运行在内核态时,它执行内核代码,所产生的线性地址大于等于0xc0000000、且所有进程共享。如果是多核CPU,那么就会出现多个进程并发访问这些地址的现象,这里就要牵扯到临界区资源,也就是我们将来在介绍同步与互斥内容中要重点讨论的。宏PAGE_OFFSET产生的值就是0xc0000000,这就是进程在线性地址空间中的偏移量,也是内核生存空间的开始之处。


页全局目录被分成了两部分,的第一部分表项映射的线性地址小于0xc0000000(共1024项,在PAE未启用时是前768项,PAE启动时是前3项),具体大小依赖特定进程。相反,剩余的表项对所有进程来说都应该是相同的,它们等于主内核页全局目录的相应表项。

 

 那么,什么又是主内核页全局目录呢?内核维持着一组自己使用的页表,驻留在所谓主内核页全局目录(master kernel Page Global Directory)中。系统初始化后,这组页表还从未被任何进程或任何内核线程直接使用,主要用来为系统中每个普通进程对应的页全局目录项提供参考模型。

 

内核如何初始化自己的页表?这个过程分为两个阶段。事实上,内核映像刚刚被装入内存后,CPU仍然运行于实模式,所以分页功能没有被启用。
第一个阶段,内核创建一个有限的地址空间,包括内核的代码段和数据段、初始页表和用于存放动态数据结构的共128KB大小的空间。这个最小限度的地址空间仅足够将内核装入RAM和对其初始化的核心数据结构。
第二个阶段,内核充分利用剩余的RAM并适当地建立分页表。

 

1.1 内核页表的初始化
 

Linux为什么要建立一个内核页表呢?Linux建立内核页表的目的有二,第一是对内核的数据结构进行动态的管理,例如将内核态进程暂时不使用的数据结构交换出去;第二是为进程的页表提供一个参考,后面的博文里还将详细讨论。

 

在系统初始化时,首先要建立一个最原始的页表,即内核临时页表。指向这个页表的临时页全局目录是在内核编译过程中静态地初始化的,而临时页表是由startup_32( )汇编语言函数(定义于arch/i386/kernel/head.S)初始化的。

 

内核编译后,临时页全局目录地址存放在swapper_pg_dir变量中。临时页表在pg0变量处开始存放,紧接在内核未初始化的数据段(这里不清楚的兄弟可以查查内存布局那一篇博文)后面。建立内核临时页表的目的就是为了初始化内核阶段提供一个映射机制。一般来说,初始化阶段内核使用的段、临时页表和128KB的内存范围能容纳于RAM前8MB空间里。那么为了映射RAM前8MB的空间,我们只需要用到两个页表,因为一个页表有1024个下标,2*1024*4K就正好是8MB。

 

分页第一个阶段的目标是允许在实模式下和保护模式下都能很容易地对这8MB寻址,目的在于做一个由实模式向保护模式的转换 。因此,内核必须创建一个映射,把从0x00000000到 0x007fffff的线性地址和从0xc0000000 到0xc07fffff的线性地址映射到从0x00000000 到 0x007fffff的物理地址。看晕了吧?那我们换个说法:内核在初始化的第一阶段,可以通过与物理地址相同的线性地址或者通过从0xc0000000开始的8MB线性地址对物理RAM的前8MB进行寻址。还晕?那没辙了,画个图再反复将上面的文字琢磨琢磨吧。

 

内核通过把swapper_pg_dir所有项都填充为0来创建期望的映射(1024项),不过,0、1、0x300(十进制的第768项)和0x301(十进制的第769项)这四项除外;后两项包含了从0xc0000000到0xc07fffff 间的,也就是从0xc0000000开始的8MB所有线性地址。0、1、0x300和0x301按以下方式初始化:
• 0项和0x300项的地址字段置为pg0的物理地址,而1项和0x301项的地址字段置为紧随pg0后的页框的物理地址。
• 把这四个项中的Present,、Read/Write和 User/Supervisor标志置位。
• 把这四个项中的Accessed 、Dirty、PCD、PWD和Page Size标志清0。

 

当建立好临时内核页表后,我们就得马上使用这个映射了,因为初始化期间,你得进入保护模式来初始化内核的各个数据结构啊,怎么使用呢?是初始化期间由汇编语言函数startup_32()来启用分页单元的:通过向cr3控制寄存器装入swapper_pg_dir的地址及设置cr0控制寄存器的PG标志来达到这一目的。下面是等价的代码片段:
movl $swapper_pg_dir-0xc0000000,%eax
movl %eax,%cr3         /*设置页表指针…*/…
movl %cr0,%eax
orl  $0x80000000,%eax
movl %eax,%cr0         /*……设置分页(PG)位*/

 

建立好临时内核页表后,我们终于可以离开实模式了,之后的故事就是利用80x86体系CPU的保护模式实现内核及各个进程的虚拟化存储管理。不过,这个内核临时页表只有8MB的映射,只是用来对初始化阶段的内核来进行寻址,还不能满足对整个内存管理的要求。那么我们进行第二步࿰

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值