分享
 
 
 

Linux在X86上的虚拟内存管理

王朝system·作者佚名  2008-05-19
窄屏简体版  字體: |||超大  

前言

Linux支持很多硬件运行平台,常用的有:Intel X86,Alpha,Sparc等。对于不能够通用的一些功能,Linux必须依据硬件平台的特点来具体实现。本文的目的是简要探讨Linux在X86保护模式上如何实现虚拟内存管理功能。为简化和方便叙述,本文做如下限定:X86处理器为80486和其后的处理器,X86工作在保护模式,不采用物理内存扩展(使用32bits物理地址),不使用扩展页(页大小为4K)。凡是与限定模式无关的内容,本文都尽量略过。Linux的虚拟内存管理中与硬件平台无关的内容在本文中也被略过。本文所援引的Linux内核源代码版本为Linux 2.2.5。

X86的分段和分页机制

I. X86的分段机制和相应系统结构

X86的分段机制就是将X86的线性地址空间分成许多小空间--段(segment),利用这些段来存储(记录)代码和数据,通过对段的保护来提供一种对数据或代码的保护。根据每个段的作用和存储内容的不同,X86将段分为三类进程段(代码段、数据段和堆栈段)和两类系统段:任务状态段(TSS,Task-State Segment)和LDT段(由于GDT不是通过段描述符和段选择符来访问,所以X86没有认为存在一个GDT段;同理,也不存在IDT段)。

在分段机制,X86使用了如下几种主要数据结构:

• 全局描述符表(GDT,Global Describtor Table):存放系统用的段描述符和各项任务共用的段描述符,可以是上述的任何一类段的段描述符,最大表长64KB;

• 局部描述符表(LDT,Local Describtor Table):存放某个任务专用的各段的段描述符,只能是三类进程段的段描述符和调用门描述符,最大表长4GB;

• 段描述符(Segment Describtor):64bits,用来描述一个段的基地址(该地址是线性地址),该段的类型,对该段操作的限制;

• 门描述符(Gate Describtor):64bits,一种特殊的描述符,为处于不同特权级的系统调用或程序的调用或访问提供保护;分为四类:调用门描述符(Call Gate Describtor)、中断门描述符(Interrupt Gate Describtor)、陷阱门描述符(Trap Gate Describtor)、任务门描述符(Task Gate Describtor);

• 段选择符(Segment Selector):16bits,用于在GDT或LDT中索引相应的段描述符;

• 中断描述表(IDT,Interrupt Describer Table):存放门描述符,只能是中断门描述符,陷阱门描述符和任务门描述符,最大表长64KB;

同时,X86提供了如下几个用于支持分段机制的寄存器:

• 全局描述符表寄存器(GDTR,GDT Register):48bits,32bits为GDT的基地址(线性地址),16bits为GDT的表长;GDTR的初始值为:基地址0,表长0xFFFF;

• 局部描述符表寄存器(LDTR,LDT Register):80bits,16bits为LDT段选择符,64bits为该LDT段的段描述符;

• 中断描述符表寄存器(IDTR,IDT Register):48bits,32bits为IDT的基地址(线性地址),16bits为IDT的表长;IDTR的初始值为:基地址0,表长0xFFFF;

• 任务寄存器(TR,Task Register):80bits,16bits为任务状态段选择符,64bits为该任务状态段的段描述符;

• 六个段寄存器(Segment Register):分为可见部分和隐藏部分,可见部分为段选择符,隐藏部分为段描述符;六个段寄存器分别为CS、SS、DS、ES、FS、GS;关于这些段寄存器的作用参见[1]中3.4.2 'Segment Register';

86工作在保护模式时,进程使用的48bits逻辑地址(Logical address)。逻辑地址的高16bits为段选择符,低32bits是段内的偏移量。通过段选择符在GDT或LDT中索引相应的段描述符(得到该段的基地址),再加上偏移量得到逻辑地址对应的线性地址(Linear Address)。如果没有采用分叶管理,线性地址是直接映射物理地址(Physical Address),于是可以直接用线性地址访问内存;否则,还要通过X86的分页转换,将线性地址转换为物理地址。

以上是对X86分段相关内容的简要描述,对于各数据结构、寄存器的细节和逻辑地址转换为线性地址的细节,请查阅 [1]。

II. X86的分页机制和相应系统结构

32bits的线性地址空间可以直接映射到物理地址空间,也可以间接映射到许多小块的物理空间(磁盘存储空间)上。这种间接映射方式就是分页机制。X86可用页大小为4KB、2MB和4MB(2MB和4MB只能在Pentium和Pentium Pro处理器中使用,本文中限定采用4KB页)。

在分页机制,X86使用了四种数据结构:

• 页目录项(PDE,Page Directory Entry):32bits结构,高20bits为页表基地址(物理地址),以4KB为递增单位,低12bits为页表属性,具体换算参见后面初始化部分;

• 页目录(Page directory):存储页目录项,位于一页中,总共可容纳1024个页目录项;

• 页表项(PTE,Page Table Entry):32bits结构,高20bits为页基地址(物理地址),低12bits为页属性;

• 页表(Page table):存储页表项,位于一页中,总共可容纳1024个页表项;

• 页(Page):4KB的连续地址空间;

为了实现分页机制和提高地址转换的效率,X86提供和使用了如下的硬件结构:

• 页标志位(PG,Page):该标志位为1,说明采用页机制;实际就是控制寄存器CR0的第31bit;

• 页缓存/快表(TLBs,Translation Lookaside Buffers):存储最近使用的PDE和PTE,以提高地址转换的效率;

• 页目录基地址寄存器(PDBR,Page Directory Base Register):用于存储页目录的基地址(物理地址),实际就是控制寄存器CR3;

为了实现将线性地址映射到物理地址,X86将32bits线性地址解释为三部分:第31bit到第22bit为页目录中的偏移,用于索引页目录项(得到对应页表的基地址);第21bit到第12bit为页表中的偏移,用于索引页表项(得到对应页的基地址);第11bit到第0bit为页中的偏移。这样,通过两级索引和页中的偏移量,最后能正确得到线性地址对应的物理地址。

关于分页机制的详细描述和作用,请查阅参考文档[1]。

LINUX的分段策略

Linux在X86上采用最低限度的分段机制,其目的是为了避开复杂的分段机制,提高Linux在其他不支持分段机制的硬件平台的可移植性,同时又充分利用X86的分段机制来隔离用户代码和内核代码。因此,在Linux上,逻辑地址和线性地址具有相同的值。

由于X86的GDT最大表长为64KB,每个段描述符为8B,所以GDT最多能够容纳8192个段描述符。每产生一个进程,Linux为该进程在GDT中创建两个描述符:LDT段描述符和TSS描述符,除去Linux在GDT中保留的前12项,GDT实际最多能容纳4090个进程。Linux的内核自身有独立的代码段和数据段,其对应的段描述符分别存储在GDT中的第2项和第3项。每个进程也有独立的代码段和数据段,对应的段描述符存储在它自己的LDT中。有关LinuxGDT表项和DLT表项分布情况参见附表1,附表2所示。

在Linux中,每个用户进程都可以访问4GB的线性地址空间。其中0x0~0xBFFFFFFF的3GB空间为用户态空间,用户态进程可以直接访问。从0xC0000000~0x3FFFFFFF的1GB空间为内核态空间,存放内核访问的代码和数据,用户态进程不能直接访问。当用户进程通过中断或系统调用访问内核态空间时,会触发X86的特权级转换(从特权级3切换到特权级0),即从用户态切换到内核态。

LINUX的分页策略

标准Linux的分页是三级页表结构,除了X86支持的页目录和页,还有一级被称为中间页目录。因此,线性地址在转换为物理地址的过程中,线性地址就被解释为四个部分(不是X86所认识的三个部分),增加了页中间目录中的索引。当运行在X86平台上时,Linux通过将中间页目录最大的页目录项个数定义为1,并提供一组相关的宏(这些宏将中间页目录用页目录来替换)将三级页面结构分解过程完美的转换为X86使用的二级页面分解。这样,无需改动内核中页面解释的主要代码(这些代码都是认为线性地址由四个部分组成)。关于这些宏定义参见Linux源码"/include/asm/pgtable.h","/include/asm/page.h"。

内核态虚拟空间从3GB到3GB+4MB的一段(对应进程页目录第768项指引的页表),被映射到物理地址0x0~0x3FFFFF(4MB)。因此,进程处于内核态时,只要通过访问3GB到3GB+4MB就可访问物理内存的低4MB空间。所有进程从3GB到4GB的线性空间都是一样的,由同样的页目录项,同样的页表,映射到相同的物理内存段。Linux以这种方式让内核态进程共享代码和数据。

Linux分段分页初始化

无论Linux系统如何被引导,经过zImage(参见arch/i386/boot/bootsect.s)或经过LILO,最后都会跳转执行arch/i386/boot/setup.s(被装载到SETUPSEG,物理地址 0x90200),setup.s从BIOS中获取计算机系统的硬件参数(如硬盘参数),放到内存参数区(临时寄放),同时做一些初步的状态检查,为进入保护模式做准备。关于引导过程和setup.s的具体执行参见[2]。

保护模式下的内核初始化模块从物理地址0x100000开始执行,该地址开始的代码和数据结构都对应在arch/i386/kernel/head.s中,参见附表3。初始化模块主要功能是对相关寄存器IDT,GDT,页目录及页表等进行初始化。下面,忽略head.s执行流程的细节,概要阐述head.s主要的初始化功能。

1. 部分寄存器的初始化:将段寄存器DS、ES、GS和FS用__KERNEL_DS(0x18,include/asm-i386/segment.h)来初始化(通过前面对段寄存器的描述和段选择符的介绍可知道,其作用是将定位到GDT中的第三项(内核数据段),并设置对该段的操作特限级为0);置位CR0的PG位,并根据CPU的型号选择置位AM, WP, NE 和 MP;用0x101000初始化CR3(页目录swapper_pg_dir的地址);置ESP高32bits为__KERNEL_DS(0x18),低32bits为init_user_stack+8192;LDTR

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有