Linux的分段分页及地址转换机制

保护模式现代操作系统的基础,理解他是我们要翻越的第一座山。保护模式是相对实模式而言的,他们是处理器的两种工作方式。很久以前大家使用的dos就是运行在实模式下,而现在的windows操作系统则是运行在保护模式下。两种运行模式有着较大的不同。
实模式由于是由8086/8088发展 而 来因此他更像是一个运行单片机的简单模式,计算机启动后首先进入的就是实模式,通过8086/8088只有20根地址线所以它的寻址范围只有2的20次 幂,即1M。内存的访问方式就是我们熟悉的seg: offset逻辑地址方式,例如我们给出地址逻辑地址它将在cpu内转换为20的物理地址,即将seg左移4位再加上offset值。例如地址 1000h:5678h,则物理地址为10000h+5678h=15678h。实模式在后续的cpu中被保留了下来,但实模式的局限性是很明显的,由于 使用seg: offset逻辑地址只能访问1M多一点的内存空间,在拥有32根地址线的cpu中访问1M以上的空间则变得很困难。而且随着计算机的不断发展实模式的工 作方式越来越不能满足计算机对资源(存储资源和cpu资源等等)的管理,由此产生了新的管理方式——保护模式。
80386及以上的处理器功能要大大超过其先前的处理器,但只有在保护模式下,处理器才能发挥作用。在保护模式下,全部32根地址线有效,可寻址4G的物 理地址空间;扩充的存储分段机制和可选的存储器分页机制,不仅为存储器共享和保护提供了硬件支持,而且为实现虚拟存储器提供了硬件支持;支持多任务;4个 特权级和完善的特权级检查机制,实现了数据的安全和保密。计算机启动后首先进入的就是实模式,通过设置相应的寄存器才能进入保护模式(以后介绍)。保护模 式是一个整体的工作方式,但分步讨论由浅入深更利于学习。
1. 关于几个寄存器和结构说明
寄存器CR0:CR0的第0位(PE)决定是否开启保护模式。0时CPU 工作于实模式,寻址方式为16位段基址*16+16位偏移地址,寻址1M空间,段基址取决于段寄存器CS/SS/DS/ES/FS/GS的内容,但与保护模式下的段式存储管理不同。CR0的第31位(PG)决定是否开启分页。1时寻址如下所述,0时线性地址即物理地址。

寄存器CR3:CR3的高20位是页目录表首地址的高20位,页目录表首地址的低20位将是0,也就是说,页目录表将以4KB对齐。类似地,PDE中的页表基址以及PTE中的页基址也是用高20位来表示4KB对齐的页表和页。

寄存器CS/SS/DS/ES/FS/GS:在保护模式下作为Seg. Selector(段选择子),在实模式下存放段基址。

寄存器GDTR/LDTR:保护模式下存放全局/局部描述符表的基址,实模式下没有。
GDT/LDT:全局描述符表/局部描述符表。
Seg. Descriptors:段描述符,作为GDT的表项。主要由三个部分组成:段基址、段界限、段属性。

2. 实模式下寻址(略)
3. 保护模式下寻址
3.1分段分页机制
1. 分段机制
80386的两种工作模式:80386的工作模式包括实地址模式和虚地址模式(保护模式)。Linux主要工作在保护模式下。
在保护模式下,80386虚地址空间可达16K个段,每段大小可变,最大达4GB。从逻辑地址到线性地址的转换由80386分段机制管理。段寄存器CS、DS、ES、SS、FS或GS标识一个段。这些段寄存器作为段选择器,用来选择该段的描述符。

图1:分段逻辑地址到线性地址转换图
clip_image001.jpg



2. 分页机制
分页机制是在段机制之后进行的,它进一步将线性地址转换为物理地址。80386使用4K字节大小的页,且每页的起始地址都被4K整除。因此,80386把4GB字节线性地址空间划分为1M个页面,采用了两级表结构。
两级表的第一级表称为页目录,存储在一个4K字节的页中,页目录表共有1K个表项,每个表项为4个字节,线性地址最高的10位(22-31)用来产生第一 级表索引,由该索引得到的表项中的内容定位了二级表中的一个表的地址,即下级页表所在的内存块号。第二级表称为页表,存储在一个4K字节页中,它包含了 1K字节的表项,每个表项包含了一个页的物理地址。二级页表由线性地址的中间10位(12-21)位进行索引,定位页表表项,获得页的物理地址。页物理地 址的高20位与线性地址的低12位形成最后的物理地址。

图2:利用两级页表转换地址
clip_image002.jpg



3. 用户空间和内核空间
用户空间:在Linux中,每个用户进程都可以访问4GB的线性虚拟内存空间。其中从0到3GB的虚存地址是用户空间,用户进程可以直接访问。
内核空间:从3GB到4GB的虚存地址为内核态空间,存放供内核访问的代码和数据,用户态进程不能访问。所有进程从3GB到4GB的虚拟空间都是一样的,linux以此方式让内核态进程共享代码段和数据段。
3.2保护模式的地址转换
保护模式下的存储方式主要体现在内存访问方式上,由于兼容和IA32框架的限制,保护模式在内存访问上延用了实模式下的seg: offset的形式(即:逻辑地址),其实seg: offset的形式在保护模式下只是一个躯壳,内部的存储方式与实模式截然不同。在保护模式下逻辑地址并不是直接转换为物理地址,而是将逻辑地址首先转换 为线性地址,再将线性地址转换为物理地址。如图一:
clip_image003.jpg



线性地址是个新概念,但大家不要把它想的过于复杂,简单的说他就是0000000h~ffffffffh(即0~4G)的线性结构,是32个bit位能表 示的一段连续的地址,但它是一个概念上的地址,是个抽象的地址,并不存在在现实之中。线性地址地址主要是为分页机制而产生的。处理器在得到逻辑地址后首先 通过分段机制转换为线性地址,线性地址再通过分页机制转换为物理地址最后读取数据。如图二:
clip_image004.jpg



分段机制是必须的,分页机制是可选的,当不使用分页的时候线性地址将直接映射为物理地址,设立分页机制的目的主要是为了实现虚拟存储。先来介绍一下分段机制,以下文字是介绍如何由逻辑地址转换为线性地址。
分段机制在保护模式中是不能被绕过得,回到我们的seg: offset地址结构,在保护模式中seg有个新名字叫做“段选择子”(seg..selector)。段选择子、GDT、LDT构成了保护模式的存储结 构,如图三,GDT、LDT分别叫做全局描述符表和局部描述符表,描述符表是一个线性表(数组),表中存放的是描述符。
clip_image005.jpg



“描述符”是保护模式中的一个新概念,它是一个8字节的数据结构,它的作用主要是描述一个段(还有其他作用以后再说),用描述表中记录的段基址加上逻辑地 址(sel: offset)的offset转换成线性地址。描述符主要包括三部分:段基址(Base)、段限制(Limit)、段属性(Attr)。一个任务会涉及多 个段,每个段需要一个描述符来描述,为了便于组织管理,80386及以后处理器把描述符组织成表,即描述符表。在保护模式中存在三种描述符表 “全局描述符表”(GDT)、“局部描述符表”(LDT)和中断描述符表(IDT)(IDT在以后讨论)。
(1) 全局描述符表GDT(Global Descriptor Table):在整个系统中,全局描述符表GDT只有一张,GDT可以被放在内存的任何位置,但CPU必须知道GDT的入口,也就是基地址放在哪 里,Intel的设计者门提供了一个寄存器GDTR用来存放GDT的入口地址,程序员将GDT设定在内存中某个位置之后,可以通过LGDT指令将GDT的 入口地址装入此积存器,从此以后,CPU就根据此寄存器中的内容作为GDT的入口来访问GDT了。GDTR中存放的是GDT在内存中的基地址和其表长界 限。 其结构如下所示:
clip_image006.jpg



(2) 段选择子(Selector)由GDTR访问全局描述符表是通过“段选择子”(实模式下的段寄存器)来完成的,如图三①步。段选择子是一个16位的寄存器(同实模式下的段寄存器相同),其结构如下所示:
clip_image007.gif



段选择子包括三部分:描述符索引(index)、TI、请求特权级(RPL)。他的index(描述符索引)部分表示所需要的段的描述符在描述符表的位 置,由这个位置再根据在GDTR中存储的描述符表基址就可以找到相应的描述符(如图三①步)。然后用描述符表中的段基址加上逻辑地址 (SEL:OFFSET)的OFFSET就可以转换成线性地址(如图三②步),段选择子中的TI值只有一位0或1,0代表选择子是在GDT选择,1代表选 择子是在LDT选择。请求特权级(RPL)则代表选择子的特权级,共有4个特权级(0级、1级、2级、3级)。例如给出逻辑地 址:21h:12345678h转换为线性地址
a. 选择子SEL=21h=0000000000100 0 01b 他代表的意思是:选择子的index=4即100b选择GDT中的第4个描述符;TI=0代表选择子是在GDT选择;左后的01b代表特权级RPL=1。
b. OFFSET=12345678h若此时GDT第四个描述符中描述的段基址(Base)为11111111h,则线性地址=11111111h+12345678h=23456789h。
(3) 局部描述符表LDT(Local Descriptor Table):局部描述符表可以有若干张,每个任务可以有一张。我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表。GDT与LDT的结构关系如下图:
clip_image008.jpg



LDT和GDT从本质上说是相同的,只是LDT嵌套在GDT之中。LDTR记录局部描述符表的起始位置,与GDTR不同LDTR的内容是一个段选择子。由 于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个表述符也会有一个选择子,LDTR装载的就是 这样一个选择子。LDTR可以在程序中随时改变,通过使用lldt指令。如图五,如果装载的是Selector 2则LDTR指向的是表LDT2。举个例子:如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h。
1. 首先需要装载LDTR使它指向LDT2 使用指令lldt将Select2装载到LDTR
2. 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是 LDT2,所以是在LDT2中选择,此时的SEL值为1Ch(二进制为11 1 00b)。OFFSET=12345678h。逻辑地址为1C:12345678h
3. 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h
4. 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector 1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)
由于每个进程都有自己的一套程序段、数据段、堆栈段,有了局部描述符表则可以将每个进程的程序段、数据段、堆栈段封装在一起,只要改变LDTR就可以实现对不同进程的段进行访问。(LDT的作用)
存储方式是保护模式的基础,学习他主要注意与实模式下的存储模式的对比,总的思想就是首先通过段选择子在描述符表中找到相应段的描述符,根据描述符中的段基址首先确定段的位置,再通过OFFSET加上段基址计算出线性地址。

clip_image004.jpg (11.75 KB)

 

clip_image004.jpg

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、 课程设计目的 本课程设计是学生学习完《计算机操作系统》课程后,进行的一次全面的综合训练,通过课程设计,让学生更好地掌握操作系统的原理及实现方法,加深对操作系统基础理论和重要算法的理解,加强学生的动手能力。 二、课程设计的内容 1、分页方式的地址换算 2、分段方式的地址换算 3、式的地址换算 三、程序运行 1、 分页地址转换: 数据: 逻辑地址:223、面大小:23 2、 分段地址转换 数据: 逻辑地址号:223、地址:23 3、 地址换算 逻辑地址号:2、号:3 四、程序源代码 #include #include int page(int A,int L ); int Segment(int sn,int sl); int SegPagt(int sn,int pn,int pd); typedef struct segtable { int segf[256]; int segl[256]; }segtable; struct segtable st; typedef struct segpagt { int segf[256]; int segl[256]; int ptl[256]; int pt[256]; int pf[256]; int pl; }segpagt; struct segpagt sp; int main() { int code; int pl,pa,sn,sd,pd,pn; //const int ptl ; int temp; do{ printf("----------------地址换算过程----------------------------\n\n"); printf(" 1.分页地址换算\n"); printf(" 2.分段地址换算\n"); printf(" 3.地址换算\n"); printf(" 4.结束运行\n\n"); printf("----------------------------------------------------------\n"); printf("请输入您的选择:"); scanf("%d",&code); switch(code) { case 1:{ printf("注意:请演示设定表长度小于\n"); printf("请输入换算的逻辑地址:\n"); scanf("%d",&pa); printf("面大小(B):\n"); scanf("%d",&pl); page(pa,pl); }break; case 2:{ printf("请演示设定表长度小于\n"); printf("请输入逻辑地址号:\n"); scanf("%d",&sn); printf("地址:\n"); scanf("%d",&sd); Segment(sn,sd); }break; case 3:{ printf("预设定表长为,面大小为\n"); printf("请输入逻辑地址号:\n"); scanf("%d",&sn); printf("号:\n"); scanf("%d",&pn); printf("地址:\n"); scanf("%d",&pd); SegPagt(sn,pn,pd); }break; case 4:{}break; } }while (code<4); } int page(int A,int L) { int d,P,kd,i; int WD; int PT[256]; for(i=1;iL) printf("号大于表长度,越界中断\n\n");//如果号大于表长度,输出越界中 else { printf("号=逻辑地址/面大小=%d,地址=逻辑地址面大小=%d\n",P,d);//输出号和地址 kd=PT[P];//根据号随机产生快号 printf("根据号%d得到块号%d\n",P,kd); WD=kd*L+d;//计算物理地址的公式 printf("物理地址=块号%d*面大小%d+地址%d\n",kd,L,d);//输出物理地址=块号*面大小+地址 printf("逻辑地址%d换算后的物理地址为%d\n\n",A,WD);//输出物理地址的结果 return (0); } }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值