当前位置首页 > 校外拓展 > 电子书

虚拟地址,逻辑地址,线性地址,她们之间如何转换?

更新时间:2024-10-03

下载地址

免费下载!

[!--downpath--]

资源介绍

本文是Intel兼容计算机(x86)的显存与保护系列文章的第一篇,延续了启动引导系列文章的主题,进一步剖析操作系统内核的工作流程。与先前一样,我将引用Linux内核的源代码,但对只给出示例(抱歉,我忽视了BSD,Mac等系统,但大部份的讨论对它们一样适用)。文中若果有错误,请不吝讨教。KM9物理好资源网(原物理ok网)

在支持Intel的显卡芯片组上,CPU对显存的访问是通过联接着CPU和南桥芯片的后端总线来完成的。在后端总线上传输的显存地址都是数学显存地址,编号从0开始仍然到可用化学显存的最低端。这种数字被南桥映射到实际的显存条上。化学地址是明晰的、最终用在总线上的编号,何必转换,何必分页,也没有特权级检测。但是,在CPU内部,程序所使用的是逻辑显存地址,它必须被转换成化学地址后,才会用于实际显存访问。从概念上讲,地址转换的过程如右图所示:KM9物理好资源网(原物理ok网)

x86CPU开启分页功能后的显存地址转换过程KM9物理好资源网(原物理ok网)

此图并未强调详尽的转换方法,它仅仅描述了在CPU的分页功能开启的情况下显存地址的转换过程。假如CPU关掉了分页功能,或运行于16位实模式,这么从分段单元(unit)输出的就是最终的化学地址了。当CPU要执行一条引用了显存地址的指令时,转换过程就开始了。第一步是把逻辑地址转换成线性地址。并且,为何不跳过这一步,而让软件直接使用线性地址(或化学地址呢?)其理由与:"人类为什么要长有胆囊?它的主要作用仅仅是被感烫发炎而已"大致相同。这是进化过程中形成的独特构造。要真正理解x86分段功能的设计,我们就必须回溯到1978年。KM9物理好资源网(原物理ok网)

最初的8086处理器的寄存器是16位的,其指令集大多使用8位或16位的操作数。这促使代码可以控制216个字节(或64KB)的显存。但是Intel的工程师们想要让CPU可以使用更多的显存,而又不用扩充寄存器和指令的显存。于是她们引入了段寄存器(),拿来告诉CPU一条程序指令将操作哪一个64K的显存区块。一个合理的解决方案是:你先加载段寄存器,相当于说"这里!我准备操作开始于X处的显存区块";以后,再用16位的显存地址来表示相对于哪个显存区块(或段)的偏斜量。总共有4个段寄存器:一个用于栈(ss),一个用于程序代码(cs),两个用于数据(ds,es)。在哪个年代,大部份程序的栈、代码、数据都可以塞入对应的段中,每段64KB长,所以分段功能时常是透明的。KM9物理好资源网(原物理ok网)

现在,分段功能仍然存在,仍然被x86处理器所使用着。每一条会访问显存的指令都隐式的使用了段寄存器。例如什么是物理地址,一条跳转指令会用到代码段寄存器(cs),一条压栈指令(stackpush)会使用到堆栈段寄存器(ss)。在大部份情况下你可以使用指令明晰的改写段寄存器的值。段寄存器储存了一个16位的段选择符();它们可以经由机器指令(例如MOV)被直接加载。惟一的例外是代码段寄存器(cs),它只能被影响程序执行次序的指令所改变,例如CALL或JMP指令。其实分段功能仍然是开启的,但其在实模式与保护模式下的运作方法并不相同的。KM9物理好资源网(原物理ok网)

在实模式下,例如在引导启动的早期,段选择符是一个16位的数值,指示出一个段的开始处的化学显存地址。这个数值必须被以某种形式放大,否则它也会受限于64K当中,分段就没有意义了。例如,CPU可能会把这个段选择符当成数学显存地址的高16位(只需将之左移16位,也就是除以216)。这个简单的规则促使:可以按64K的段为单位,一块块的将4GB的显存都轮询到。遗憾的是,Intel做了一个很奇特的设计,让段选择符仅仅除以24(或16),一举将轮询范围限制在了1MB,还引入了过度复杂的转换过程。下列图例显示了一条跳转指令,cs的值是:KM9物理好资源网(原物理ok网)

实模式分段功能KM9物理好资源网(原物理ok网)

实模式的段地址以16个字节为步长,从0开始编号仍然到(即1MB)。你可以将一个从0到的16位偏斜量(逻辑地址)加在段地址上。在这个规则下,对于同一个显存地址,会有多个段地址/偏斜量的组合与之对应,但是化学地址可以超过1MB的边界,只要你的段地址足够高(参见臭名昭著的A20线)。同样的,在实模式的C语言代码中,一个远表针(far)既包含了段选择符又包含了逻辑地址,用于轮询1MB的显存范围。这麽"远"的啊。随着程序显得越来越大,超出了64K的段,分段功能以及它奇特的处理方法,致使x86平台的软件开发显得十分复杂。这些设定可能听上去有些怪异,但它却把当时的程序员加快了令人崩溃的深渊。KM9物理好资源网(原物理ok网)

在32位保护模式下,段选择符不再是一个单纯的数值,取而代之的是一个索引编号,用于引用段描述符表中的表项。这个表为一个简单的链表,元素宽度为8字节,每位元素描述一个段。看上去如下:KM9物理好资源网(原物理ok网)

段描述符KM9物理好资源网(原物理ok网)

有三种类型的段:代码,数据,系统。为了简约明了,只有描述符的共有特点被勾画下来。基地址(base)是一个32位的线性地址,指向段的开始;段界限(limit)强调这个段有多大。将基地址加到逻辑地址上就产生了线性地址。DPL是描述符的特权级(level),其值从0(最高特权,内核模式)到3(最低特权,用户模式),用于控制对段的访问。KM9物理好资源网(原物理ok网)

这种段描述符被保存在两个表中:全局描述符表(GDT)和局部描述符表(LDT)。笔记本中的每一个CPU(或一个处理核心)都富含一个称作gdtr的寄存器,用于保存GDT的首个字节所在的线性显存地址。为了选出一个段,你必须向段寄存器加载符合以下格式的段选择符:KM9物理好资源网(原物理ok网)

段选择符KM9物理好资源网(原物理ok网)

对GDT,TI位为0;对LDT,TI位为1;index强调想要表中哪一个段描述符(今译:原文是段选择符,应当是疏漏)。对于RPL,恳请特权级(Level),之后我们都会详尽讨论。如今,须要好好想想了。当CPU运行于32位模式时,不管怎么,寄存器和指令都可以轮询整个线性地址空间,所以根本就不须要再去使用基地址或其他哪些鬼东西。那为何不干脆将基地址设成0,好让逻辑地址与线性地址一致呢?Intel的文档将之称为"扁平模型"(flatmodel),并且在现代的x86系统内核中就是如此做的(非常强调,它们使用的是基本扁平模型)。基本扁平模型(basicflatmodel)等价于在转换地址时关掉了分段功能。这么一来多么美好啊。就让我们来瞧瞧32位保护模式下执行一个跳转指令的反例,其中的数值来自一个实际的Linux用户模式应用程序:KM9物理好资源网(原物理ok网)

保护模式的分段KM9物理好资源网(原物理ok网)

段描述符的内容一旦被访问,都会被cache(缓存),所以在此后的访问中,就不再须要去实际读取GDT了,否则会有损性能。每位段寄存器都有一个隐藏部份用于缓存段选择符所对应的那种段描述符。假如你想了解更多细节,包括关于LDT的更多信息,请参阅《IntelGuide》3A卷的第三章。2A和2B卷述说了每一个x86指令,同时也指明了x86轮询时所使用的各种类型的操作数:16位,16位加段描述符(可被用于实现远表针),32位,等等。KM9物理好资源网(原物理ok网)

在Linux上,只有3个段描述符在引导启动过程被使用。她们使用宏来定义并储存在链表中。其中两个段是扁平的,可对整个32位空间轮询:一个是代码段,加载到cs中,一个是数据段,加载到其他段寄存器中。第三个段是系统段,称为任务状态段(TaskState)。在完成引导启动之后,每一个CPU都拥有一份属于自己的GDT。其中大部份内容是相同的,只有少数表项依赖于正在运行的进程。你可以从.h见到LinuxGDT的布局以及其实际的样子。这儿有4个主要的GDT表项:2个是扁平的,用于内核模式的代码和数据,另两个用于用户模式。在看这个LinuxGDT时,请留心这些用于确保数据与CPU缓存线对齐的填充字节——目的是克服冯·诺依曼困局。最后要谈谈,那种精典的Unix错误信息"fault"(分段错误)并不是由x86风格的段所造成的,而是因为分页单元检查到了非法的显存地址。哎呦什么是物理地址,上次再讨论这个话题吧。KM9物理好资源网(原物理ok网)

Intel巧妙的绕开了她们原本设计的那种拼堆砌凑的分段方式,而是提供了一种富有弹性的方法来让我们选择是使用段还是使用扁平模型。因为很容易将逻辑地址与线性地址合二为一,于是这成为了标准,例如现今在64位模式中就强制使用扁平的线性地址空间了。并且即便是在扁平模型中,段对于x86的保护机制也极其重要。保护机制用于抵挡用户模式进程对系统内核的非法显存访问,或各个进程之间的非法显存访问,否则系统将会步入一个狗咬狗的世界!在下一篇文章中,我们将窥探保护级别以及怎样用段来实现这种保护功能。KM9物理好资源网(原物理ok网)

发表评论

最新列表

最热列表

统计代码放这里