`
jjchen_lian
  • 浏览: 86438 次
  • 性别: Icon_minigender_1
  • 来自: 广州
社区版块
存档分类
最新评论

X86分段机制

 
阅读更多
内存分段机制有很多好处,不仅方便程序的重定位以及方便内存管理等等的好处,我们就从内存的重定位来分析分段机制的作用。
指令和指令集:
简单地说,处理器的设计者用某些数来指示处理器所进行的操作,这成为指令,或者叫机器指令,因为只有处理器才认得他们。比如,指令F4H表示让处理器停机,当处理器取到并执行这条指令后,就停止工作。指令是集中存放在内存里的,一条接着一条,处理器的工作是自动按顺序取出并加以执行。如下所示,从内存地址0000H开始(也就是内存地址的最低端)连续存放了一些指令,同时,假定执行这些指令的是一个16位处理器,拥有两个16位的寄存器RA和RB
一般来说,指令由操作码和操作数构成,但也有小部分指令仅有操作码,而不含操作数,如图所示,停机指令仅包含1字节的操作码F4,而没有操作数,指令的长度不定,短的指令仅有1字节,而长的指令则有可能大道15字节。
对处理器来说,指令的操作码隐含了如何执行该指令的信息,比如它是做什么的,以及怎么去做,第一条指令的操作码是B8,这表明,该指令是一条传送指令,第一个操作数是寄存器,第二个操作数是直接包含在指令中的,紧跟在操作码之后,可以立即从指令中取得,所以叫做立即数。同时,操作码还直接指出该寄存器是RA。RA是16位寄存器,这条指令将按字进行操作。所以,当这条指令执行之后,该指令的操作数(立即数)005DH就被传送到RA中。
对于复杂一些的指令来说,1个字节的操作码可能不会够用,所以,第2跳指令的操作码为8B1E,它隐含的意思是,这是一条传送指令,第一个操作数是寄存器,而且是RB寄存器,第二个操作数是内存地址,要传送到RB寄存器中的数存放在该地址中,同时,这是一个字操作指令,应当从第二个操作数指定的地址中取出一个字。
该指令的操作数部分是3F00,指定了一个内存地址003FH。它相当于高级语言里的指针,当处理器执行这条指令时,会再次用003FH作为地址去访问内存,从那里取出一个字(1002H),然后将它传送到寄存器RB。注意,“传送”这个词带有误导性,其实,传送的意思更像是“复制",传送之后,003FH单元里的数据还保持原样。
通过这两条指令的比较,很容易分清指令中的“立即数”是什么意思。指令执行和操作的对象是数。如果这个数已经在指令中给出,不需要再次访问内存,那这个数就是立即数,比如第一条指令中的005DH;相反,如果指令中给出的是地址,真正的数还需要用这个地址访问内存才能得到,那么它就不能称为立即数,比如第二条指令中的003FH。
 
我们知道,处理器是自动化的器件,在给出了起始地址之后,它将从这个地址开始,自动地取出每条指令并加以执行,只要每条指令都正确无误,它就能准确地知道下一条指令的地址。这就意味着,完成某个工作的所有指令,必须集中在一起,处于内存的某个位置,形成一个段,叫做代码段,事情是明摆着的,要是指令并没有一条挨着一条存放,中间夹杂了其他非指令的数据,处理器就因为不能识别而出错。
为了做某件事而编写的指令,它们一起形成了我们平时所说的程序,程序总要操作大量的数据,这些数据也应该集中在一起,位于内存中的某个地方,形成一个段,叫做数据段。
段在内存中的位置并不重要,因为处理器是可控的,我们可以让它从内存的任何位置开始取指令并加以执行。这里有一个例子,如图所示,我们有一大堆数字,现在想把它们加起来求出一个总和。假定我们有16个数要相加,这些数都是16位的二进制,分别是0005H,00A0H,00FFH,...。为了让处理器把它们加起来,我们应该先在内存中定义一个数据段,将这些数字写进去,数据段可以起始于内存中的任何位置,既然如此,我们将它定义在0100H处,这样一来,第一个要加的数位于地址0100H,第二个要加的数位于地址0102H,最后一个数的地址是011EH。
一旦定义了数据段,我们就知道了每个数的内存地址,然后,紧挨着数据段,我们从内存地址0120H处定义代码段,严格来说,数据段和代码段是不需要连续的,但这里我们把它们挨在一起更自然一些,为了区别数据段和代码段,我们使用了不同的底色。
代码段是从内存地址0120H开始的,第一条指令是A10001,其功能是将内存单元0100H里的字传送到AX寄存器,指令执行后,AX的内容为0005H。
第二条指令是03060201,功能是将AX中的内容和内存单元0102H里的字相加,结果在AX中。由于AX的内容为0005H,而内存地址0102H里的数是00A0H,这条指令执行后,AX的内容是00A5H。
第三条指令是03060401,功能是将AX中的内容和内存单元0104H里的字相加,结果在AX中,此时,由于AX里的内容是00A5H,内存地址0104H里的数是00FFH,本指令执行后,AX的内容为01A4H。
后面的指令跟前2条相似,依次用AX的内容和下一个内存单元里的字相加,一直到最后,在AX中得到总的累加和,在这个例子中,我们没有考虑AX寄存器容纳不下结果的情况。当累加的总和超过了AX所能表示的数的范围(最大为FFFFH,即十进制的65535)时,就会产生进位,但这个进位被丢弃。
在内存中定义了数据段和代码段之后,我们就可以指令处理器从内存地址0120H处开始执行,当所有的指令执行完后,就能在AX寄存器中得到最后的结果。


 
看起来好像没有问题,很完美。但是仔细想一想,还是能感觉到会有问题存在。
在前面的例子中,所有在执行时需要访问内存单元的指令,使用的都是真实的内存地址。比如A10001,这条指令的意思是从地址为0100H的内存单元里取出一个字,并传送到寄存器AX。在这里,0100H是一个真实的内存地址,又称物理地址。
整个程序(包括代码段和数据段)在内存中的位置,是由我们自己定的。我们把数据段定在0100H,把代码段定在0120H。
问题是,大多数时候,整个程序(包括代码段和数据段)在内存中的位置并不是我们能够决定的。请想一想你平时是怎么使用计算机的,你所用的程序,包括那些用来调整计算机性能的工具、小游戏、音乐和视频播放器等,都是从网上下载的,位于你的硬盘、盘或光盘中。即使有些程序是你自己编写的,那又如何?当你双击它们的图标,使它们在里启动之前,内存已经被塞了很多东西,就算你是刚刚打开计算机,自己已经占用了很多内存空间,不然的话,你怎么可能在它上面操作呢?
在这种情况下,你所运行的程序,在内存中被加载的位置完全是随机的,哪里有空闲的地方,它就会被加载到哪里,并从那里开始被处理器执行。所以,前面那段程序不可能恰好如你所愿,被加载到内存地址,它完全可能被加载到另一个不同的位置,比如。但是,同样是那个程序,一旦它在内存中的位置发生了改变,灾难就出现了。
如图所示,因为程序现在是从内存地址处被加载的,所以,数据段的起始地址为。这就是说,第一个要加的数,其地址为,第二个则为,其他依次类推。代码段依然紧挨着数据段之后,起始地址相应地是1020H。
只要所有的指令都是连续存放的,代码段位于内存中的什么地方都可以正常执行。所以,处理器可以按你的要求,从内存地址处连续执行,但结果完全不是你想要的。
请看第一条指令,它的意思是从内存地址处取得一个字,将其传送到寄存器。但是,由于程序刚刚改变了位置,它要取的那个数,现在实际上位于,它取的是别人地盘里的数!
这能怪谁呢?发生这样的事情,是因为我们在指令中使用了绝对内存地址(物理地址),这样的程序是无法重定位的。为了让你写的程序在卖给别人之后,可以在内存中的任何地方正确执行,就只能在编写程序的时候使用相对地址或者逻辑地址了,而不能使用真实的物理地址。当程序加载时,这些相对地址还要根据程序实际被加载的位置重新计算。   


 
那么这种问题如何解决呢?
而此时,内存分段机制就应运而生了。如图所示,整个内存间就像长长的纸条,在内存中分段,就像从长纸条中裁下一小段来。根据需要,段可以开始于内存中的任何位置,比如图中的内存地址A532H处。 在这个例子中,分段开始于地址为A532H的内存单元处,这个起始地址就是段地址。这个分段包含了6个存储单元。在分段之前,它们在整个内存空间里的物理地址分别是A532H、A533H、A534H、A535H、A536H、A537H。但是,在分段之后,它们的地址可以只相对于自己所在的段。这样,它们相对于段开始处的距离分别为0、1、2、3、4、5,这叫做偏移地址。于是,当采用分段策略之后,一个内存单元的地址实际上就可以用“段:偏移”或者 “段地址:偏移地址”来表示,这就是通常所说的逻辑地址。比如,在图2-10中,段内第1个存储单元的地址为A532H:0000H,第3个存储单元的地址为A532H:0002H,而本段最后一个存储单元的地址则是A532H:0005H。为了在硬件一级提供对“段地址:偏移地址”内存访问模式的支持,处理器至少要提供两个段寄存器,分别是代码段(Code Segment,CS)寄存器和数据段(Data Segment,DS)寄存器。对CS内容的改变将导致处理器从新的代码段开始执行。同样,在开始访问内存中的数据之前,也必须首先设置好DS寄存器,使之指向数据段。除此之外,最重要的是,当处理器访问内存时,它把指令中指定的内存地址看成是段内的偏移地址,而不是物理地址。这样,一旦处理器遇到一条访问内存的指令,它将把DS中的数据段起始地址和指令中提供的段内偏相加,来得到访问内存所需要的物理地址。
  


如图所示,代码段的段地址为1020H,数据段的段地址为1000H。在代码段中有一条指令A1 02 00,它的功能是将地址0002H处的一个字传送到寄存器AX。在这里,处理器将0002H看成是段内的偏移地址,段地址在DS中,应该在执行这条指令之前就已经用别的指令传送到DS中了。当执行指令A1 02 00时,处理器将把DS中的内容和指令中指定的地址0002H相加,得到1002H。这是一个物理地址,处理器用它来访问内存,就可以得到所需要的数00A0H。如果一下次执行这个程序时,代码段和数据段在内存中的位置发生了变化,只要把它们的段地址分别传送到CS和DS,它也能够正确执行。   

8086内部有4个段寄存器。其中,CS是代码段寄存器,DS是数据段寄存器,ES是附加段(Extra Segment)寄存器。附加段的意思是,它是额外赠送的礼物,当需要在程序中同时使用两个数据段时,DS指向一个,ES指向另一个。可以在指令中指定使用DS和ES中的哪一个,如果没有指定,则默认是使用DS。SS是栈段寄存器,以后会讲到,而且非常重要。IP是指令指针(Instruction Pointer)寄存器,它只和CS一起使用,而且只有处理器才能直接改变它的内容。当一段代码开始执行时,CS指向代码段的起始地址,IP则指向段内偏移。这样,由CS和IP共同形成逻辑地址,并由总线接口部件变换成物理地址来取得指令。然后,处理器会自动根据当前指令的长度来改变IP的值,使它指向下一条指令。当然,如果在指令的执行过程中需要访问内存单元,那么,处理器将用DS的值和指令中提供的偏移地址相加,来形成访问内存所需的物理地址。
8086的段寄存器和IP寄存器都是16位的,如果按照原先的方式,把段寄存器的内容和偏移地址直接相加来形成物理地址的话,也只能得到16位的物理地址。麻烦的是,8086却提供了20根地址线。换句话说,它提供的是20位的物理地址。提供20位地址线的原因很简单,16位的物理地址只能访问64KB的内存,地址范围是0000H~FFFFH,共65536个字节。这样的容量,即使是在那个年代,也显得捉襟见衬。注意,这里提到了一个表示内存容量的单位“KB”。为了方便,我们通常使用更大的单位来描述存容量,比如千字节(KB)、兆字节(MB)和吉字节(GB),它们之间的换算关系如下:
1 KB = 1024 Byte
1 MB = 1024 KB 
1 GB = 1024 MB
所以,65536 个字节就是64KB,而20 位的物理地址则可以访问多达1MB 的内存,地址范围从00000H 到FFFFFH。问题是,16 位的段地址和16 位的偏移地址相加,只能形成16 位的物理地址,怎么得到这20 位的物理地址呢? 为了解决这个问题,8086 处理器在形成物理地址时,先将段寄存器的内容左移4 位(相当于乘以十六进制的10,或者十进制的16),形成20 位的段地址,然后再同16 位的偏移地址相加,得到20 位的物理地址。比如,对于逻辑地址F000H:052DH,处理器在形成物理地址时, 将地址F000H 左移4 位,变成F0000H,再加上偏移地址052DH,就形成了20 位的物理地址F052DH。这样,因为段寄存器是16 位的,在段不重叠的情况下,最多可以将1MB 的内存分成65536 个段,段地址分别是0000H、0001H、0002H、0003H,……,一直到FFFFH。
在这种情况下, 如图2-13 所示,每个段正好16 个字节,偏移地址从0000H 到000FH。 同样在不允许段之间重叠的情况下,每个段的最大长度是64KB,因为偏移地址也是16 位的,从0000H 到FFFFH。在这种情况下,1MB 的内存,最多只能划分成16 个段,每段长64KB, 段地址分别是0000H、1000H、2000H、3000H,…,一直到F000H。 以上所说的只是两种最典型的情况。通常情况下,段地址的选择取决于内存中哪些区域是空闲的。
举个例子来说,假如从物理地址00000H开始,一直到82251H处都被其他程序占用着,而后面一直到FFFFFH的地址空间都是自由的,那么,你可以从物理内存地址82251H之后的地方加载你的程序。 接着,你的任务是定义段地址并设置处理器的段寄存器,其中最重要的是段地址的选取。因为偏移地址总是要求从0000H开始,而82260H是第一个符合该条件的物理地址,因为它恰好对应着逻辑地址8226H:0000H,符合偏移地址的条件,所以完全可以将段地址定为8226H。
举个例子来说,如果你从物理内存地址82255H处加载程序,由于它根本无法表示成一个偏移地址为0000H的逻辑地址,所以不符合要求,段不能从这里开始划分。这里面的区别在于,82260H可以被十进制数16(或者十六进制数10H)整除,而82255H不能。通过这个例子可以看出,8086处理器的逻辑分段,起始地址都是16的倍数,这称为是按16字节对齐的。段的划分是自由的,它可以起始于任何16字节对齐的位置,也可以是任意长度,只要不超过64KB。比如,段地址可以是82260H,段的长度可以是64KB。在这种情况下,该段所对应的逻辑地址范围是8226H:0000H~8226H:FFFFH,其所对应的物理地址范围是82260~9225FH。同时,正是由于段的划分非常自由,使得8086的内存访问也非常随意。同一个物理地址,或者同一片内存区域,根据需要,可以随意指定一个段来访问它,前提是那个物理地址位于该段的64KB范围内。也就是说,同一个物理地址,实际上对应着多个逻辑地址。

 上面的内容是这次分段机制的全部内容,大部分来自<<x86汇编-从实模式到保护模式一书>>,后面要开始学习<<30天自制操作系统>>一书中的第一个例子了。

分享到:
评论

相关推荐

    linux x86内存管理之分段与分页

    x86 的分段机制从 80286 开始就存在,存在两种地址转换模式:实模式(Real Mode)和保护模式(Protected Mode)。PC 上电复位时,x86 处于实模式下,需要转换到保护模式时,需要做些准备工作。 逻辑地址到线性地址...

    linux 分页分段机制及储存分析

    在深入探讨Linux操作系统中的分页分段机制之前,我们首先要理解这两个术语的基本概念。分页和分段是现代操作系统管理内存的两种主要方法,它们帮助系统有效地管理和分配虚拟内存,提高资源利用率,并确保进程间的...

    Linux的分段和分页机制

    #### 一、分段机制 ##### 1.1 80386的两种工作模式 在探讨Linux的分段机制之前,我们先了解一下80386处理器的两种工作模式:实地址模式和保护模式。 - **实地址模式**:这是系统启动时的初始状态,也是较早的x86...

    Linux内存管理之虚拟内存管理.doc

    X86处理器的分段机制和分页机制是Linux操作系统虚拟内存管理机制的基础,X86处理器的分段机制将逻辑地址空间分割成许多小的段,各个段可以存储不同的代码和数据,并且可以对这些段进行保护。X86处理器的分页机制将...

    x86保护模式的介绍

    3. **分段机制**:虽然386保护模式中段机制的作用相对较小,但在早期的x86架构中,段机制用于实现内存的逻辑划分。每个段都有一个基地址和长度,通过段选择子和偏移地址来确定实际内存位置。 4. **多任务支持**:...

    x86汇编语言从实模式到保护模式(完整版)更新过

    1. **内存管理与分段**:这部分会详细介绍x86的分段机制,包括段寄存器、段选择符和段基址的概念,以及如何通过它们来形成有效的内存地址。还会涉及段限长和段描述符表等概念。 2. **分页机制**:介绍如何通过分页...

    操作系统源码 x86

    1. 分区与分段:早期的x86操作系统可能使用分区或分段进行内存管理,但现代操作系统通常使用分页技术。 2. 虚拟内存:通过分页和页表,实现虚拟地址到物理地址的转换,提供更大的地址空间和内存保护。 3. 内存分配与...

    第三章x86的虚拟存储系统.pdf

    x86架构的分页机制作为分段机制的补充,进一步将线性地址空间分割成固定大小的页。每个页可以映射到物理内存中的任意位置,或者在必要时被交换到硬盘上。这种设计不仅提高了内存的利用率,还增强了操作系统的内存...

    x86汇编语言-从实模式到保护模式源码及工具

    《x86汇编语言-从实模式到保护模式源码及工具》是一份深入学习x86架构汇编语言的重要资源,它涵盖了从实模式到保护模式的过渡,这在理解计算机系统底层运行机制中至关重要。实模式是8086处理器的初始工作模式,而...

    x86汇编语言:从实模式到保护模式.zip

    保护模式是x86处理器的一个重大改进,引入了内存分段和分页机制,从而提高了系统的安全性和多任务处理能力。在保护模式下,每个进程都有自己的独立虚拟地址空间,可以防止一个进程意外地修改或访问其他进程的数据。...

    X86处理器发展史

    此外,8086还支持分段机制,这为后来的操作系统设计提供了基础。 #### 前苏联复制8086 - **背景**:冷战期间,前苏联通过工业间谍活动获取了8086的技术细节,并据此生产了自己的版本——K1810BM86。 - **意义**:...

    x86汇编语言++从实模式到保护模式

    3. 内存管理:理解实模式的线性地址和保护模式下的分段分页机制,以及如何通过这些机制来管理和保护内存。 4. 模式切换:学习如何在代码中实现实模式到保护模式的切换,包括设置控制寄存器和初始化内存管理结构。 5....

    x86 汇编语言:从实模式到保护模式

    4. 分段机制:详细讲解段选择子、段基址、段限长以及它们如何组合成线性地址。 5. 分页机制:解释页表、页目录和虚拟地址到物理地址的转换过程。 6. 权限和访问控制:讨论各段和页的访问权限,如读写、执行权限以及...

    《x86汇编语言:从实模式到保护模式》配套的完整资料

    保护模式则引入了分段和分页机制,使得处理器可以支持更大内存、实现内存保护和多任务并行。书中详细解释了如何设置和使用段寄存器、描述符表、分页机制,以及保护模式下的中断和异常处理。这些内容对于理解现代操作...

    X86汇编语言:从实模式到保护模式

    保护模式是X86处理器的一个重大进步,引入了分页和分段机制,允许更高级别的内存管理和保护。这一模式允许操作系统创建多个独立的地址空间,实现进程隔离和权限控制。书中会详细介绍保护模式下的段描述符表和全局...

    x86汇编语言从实模式到保护模式 书+源代码+调试工具教程

    《x86汇编语言从实模式到保护模式》是一本深入探讨x86架构下计算机操作系统的底层机制的教程。这本书涵盖了从实模式到保护模式的转变,这是理解现代个人计算机工作原理的关键一步。实模式是8088/8086处理器的初始...

    x86体系结构的OS开发

    在x86系统中,内存管理机制包括分页和分段,这两者都是为了提高内存的利用率和安全性。中断处理是操作系统响应硬件事件(如键盘输入或时钟中断)的关键机制,而中断向量表则存储了处理这些事件的地址。 在进程和...

Global site tag (gtag.js) - Google Analytics