`
jjchen_lian
  • 浏览: 86204 次
  • 性别: 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...

    X86体系中保护模式下的内存访问机制

    X86体系结构中保护模式下的内存访问机制主要包括分段机制和分页机制。分段机制通过将线性地址空间划分为多个段,实现了对代码和数据的有效管理;而分页机制则进一步将虚拟地址空间划分为固定大小的页面,提高了内存...

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

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

    x86保护模式的介绍

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

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

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

    操作系统源码 x86

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

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

    随着处理器的发展,80286引入了保护模式,其最大特点是引入了分段和分页机制,提供了内存保护、任务隔离和虚拟内存等功能。每个进程都有独立的地址空间,可以运行在自己的虚拟地址空间内,互不影响。此外,保护模式...

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

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

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

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

    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