您的位置:首页 > 运维架构 > Linux

Linux内存管理总结-系统初始化

2013-02-02 16:52 393 查看
操作系统如何为每个进程营造一个虚拟地址空间呢,写到这里不禁想起骇客帝国的场景,Matrix就是为人类虚拟出一个世界,让人们乐在其中。
首先,我们要从操作系统初始化讲起。
操作系统一般把物理内存分成4K大小一个的页框(PageFrame).系统启动最开始的一瞬,物理内存都是空闲的,好似一批蛮荒。



启动后,各路创世神灵(BIOS,各种硬件)开始占地盘。首先是BIOS,BIOS会把系统启动时检查到的硬件配置存放到页框0中。



物理地址从0x000a0000到0x000fffff的范围通常也留给BIOS例程,并且映射ISA图形卡上的内部内存。由于BIOS和各路硬件大神启动时通常占据起始1MB空间内的页框(图中黑色部分表示被占用)。内核程序为了避免冲突和保障内存的连续性,退让到从物理内存1MB处开始加载。



X86结构中,Linux内核代码加上初始化必要的一些数据,被装载在物理内存从1M开始的地址上(1M以内的地址留给BIOS或者其他特殊的用途)。虽然内核的大小是不确定的,但通常0-8M的地址范围足够保存这些内容。这些内容依次为:内核代码、已初始化的内核数据、未初始化的内核数据。



内核启动过程中,存在一个实模式保护模式的切换过程。在linux启动的最初阶段,内核刚刚被装入内存时,分页功能还未启用,此时是直接存取物理地址的(或者说线性地址就等于物理地址)。但初始化完成后,内核也需要有自己的虚拟地址空间(1个G大小),该虚拟地址空间的地址映射关系,会被作为模版拷贝到其他进程的内核地址空间中。



在开启分页功能之前,内核必须初始化页全局目录以及页表,称为内核页全局目录和内核页表。这个初始化分为两个阶段:
阶段一是建立临时内核页表。
阶段二是建立最终内核页表。
(1)临时内核页表
临时内核页表只用来映射物理地址的前8M空间内容。目的是允许CPU在实模式(直接存取物理地址)和保护模式(根据虚拟地址映射)之间切换的过程中,都能对这前8M的地址进行访问。(假如内核使用的全部内存可以存放在8M的空间里,因为一个页表可以映射4M的地址,所以8M的空间需要两个页表,也就是需要两个页目录项。这两张页表我们称为临时内核页表pg0和pg1。(页表的作用,参见地址映射))

临时页全局目录存放在swappper_pg_dir变量中,临时页表在pg0和pg1变量出存放。紧接着在内核未初始化的数据段后面。通过查看/boot/System.map,可以找到swapper_pg_dir的地址,例如0xc047d000 (处于第1149个页框。抄了某位前辈的数据,由于不知道原始作者是谁,就先在这声明下。这个数据是编译时产生的所以,因机器而异,后面的数据同此处)。pg0的线性地址为0xc04f4000(处于第1268个页框)



因为分页第一阶段的目的是实模式下与保护模式下都可以访问这8M的地址。所以内核需要将0x00000000到0x007fffff的线性地址与从0xc0000000到0xc07fffff的线性地址都映射到从0x00000000到0x007fffff的物理地址。



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

movl $swapper_pg_dir-0xc0000000,x

movl x,%cr3 /*设置页表指针…*/…

movl %cr0,x

orl $0x80000000,x

movl x,%cr0 /*……设置分页(PG)位*/

建立好临时内核页表后,我们终于可以离开实模式了,进入虚拟地址空间。

(2)最终内核页表
临时内核页表解决了前8M物理内存的映射问题,而最终内核页表需要包含更多当前可用物理内存的映射关系,最终内核页表还是使用swapper_pg_dir指向的页目录表。我们知道在临时内核页表初始化阶段,该页目录表只有4项是非0的。那么在最终内核页表初始化阶段,需要将剩余表项利用上。但是内核地址有效空间只有1G,从0x0xC0000000。因此只需要将0xC0000000-0xFFFFFFFF直接的线性地址与物理地址的对应关系映射完成,也就是将页目录表的第769项-1024项填充完成(实际只映射896M物理内存,原因专门解释)。
最终填充完成后的内核线性空间与物理地址空间的映射关系可用下图来描述。
内核线性空间的0-8M地址段映射到物理地址空间的0-8M地址段。
内核线性空间的8M以上到3GB处均无映射,属于不可使用。
内核线性空间的从0xC0000000开始的896MB空间直接映射物理地址空间0-896MB地址段。
直接映射的含义是:内核虚拟地址减去一个常数0xC0000000,即是物理地址。



我们看到内核线性地址第四个GB的前896MB部分映射系统的物理内存。但是,至少128MB的线性地址总是留作他用,因为内核使用这些线性地址实现非连续内存分配 和固定映射的线性地址 。
(待续...)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: