Intel段寄存器

一、 段寄存器的产生

段寄存器的产生源于Intel 8086 CPU体系结构中数据总线与地址总线的宽度不一致。

数据总线的宽度,也即是ALU(算数逻辑单元)的宽度,平常说一个CPU是“16位”或者“32位”指的就是这个。8086CPU的数据总线是16位。

地址总线的宽度不一定要与ALU的宽度相同。因为ALU的宽度是固定的,它受限于当时的工艺水平,当时只能制造出16位的ALU;但地址总线不一样,它可以设计得更宽。地址总线的宽度如果与ALU相同当然是不错的办法,这样CPU的结构比较均衡,寻址可以在单个指令周期内完成,效率最高;而且从软件的解决来看,一个变量地址的长度可以用整型或者长整型来表示会比较方便。

但是,地址总线的宽度还要受制于需求,因为地址总线的宽度决定了系统可寻址的范围,即可以支持多少内存。如果地址总线太窄的话,可寻址范围会很小。如果地址总线设计为16位的话,可寻址空间是2^16=64KB,这在当时被认为是不够的;Intel最终决定要让8086的地址空间为1M,也就是20位地址总线。

地址总线宽度大于数据总线会带来一些麻烦,ALU无法在单个指令周期里完成对地址数据的运算。有一些容易想到的可行的办法,比如定义一个新的寄存器专门用于存放地址的高4位,但这样增加了计算的复杂性,程序员要增加成倍的汇编代码来操作地址数据而且无法保持兼容性。

Intel想到了一个折中的办法:把内存分段,并设计了4个段寄存器,CS,DS,ES和SS,分别用于指令、数据、其它和堆栈。把内存分为很多段,每一段有一个段基址,当然段基址也是一个20位的内存地址。不过段寄存器仍然是16位的,它的内容代表了段基址的高16位,这个16位的地址后面再加上4个0就构成20位的段基址。而原来的16位地址只是段内的偏移量。这样,一个完整的物理内存地址就由两部分组成,高16位的段基址和低16位的段内偏移量,当然它们有12位是重叠的,它们两部分相加在一起,才构成完整的物理地址。

Baseb15 ~ b12b11 ~ b0 
Offset o15 ~ o4o3 ~ o0
Addressa19 ~ a0

这种寻址模式也就是“实地址模式”。在8086中,段寄存器还只是一个单纯的16位寄存器,而且操作寄存器的指令也不是特权指令。通过设置段寄存器和段内偏移,程序就可以访问整个物理内存,无安全性可言。

总之一句话,段寄存器的设计是一个权宜之计,现在看来可以说是一个临时性的解决方案,设计它的目的是为了把地址空间从64KB扩展为1MB,仅此而已。但是它的加入却为日后Intel系列芯片的发展带来诸多不便,也为理解i386体系带来困扰。

二、 实现保护模式

到了80386问世的时候,工艺已经有了很大的进步,386的ALU有已经从16位跃升为32位,也就是说,38086是32位的CPU,而且结构也已经比较成熟,接下来的80486一直到Pentium系列虽然速度提高了几个数量级,但并没有质的变化,所以被统称为i386结构。

对于32位的CPU来说,只要地址总线宽度与数据总线宽度相同,就可以寻址2^32=4GB的内存空间,这已经足够用,已经不再需要段寄存器来帮助扩展。但这时Intel已经无法把段寄存器从产品中去掉,因为新的CPU也是产品系列中的一员,根据兼容性的需要,段寄存器必须保留下来。

这时,技术的发展需求Intel在其CPU中实现“保护模式”,用户程序的可访问内存范围必须受到限制,不能再任意地访问内存所有地址。Intel决定利用段寄存器来实现他们的保护模式,把保护模式建立在段寄存器的基础之上。

对于段的描述不再只是一个20位的起始地址,而是全新地定义了“段描述项”。段描述项的结构如下:
B31 ~ B24DES1 (4 bit)L19 ~ L16
DES2 (8 bit)B23 ~ B16
B15 ~ B0
L15 ~ L0

每一行是两个字节,总共8个字节,64位。

DES1和DES2分别是一些描述信息,用于描述本段是数据段还是代码段,以及读写权限等等。B0~B31是段的基地址,L0~L19是段的长度。

注意,规定段的长度是非常必要的,如果不限定段长度,“保护”就无从谈起,用户程序的访问至少不能超过段的范围。另外,段长度只有20位,所代表的最大可能长度为2^20=1M,而整个地址空间是2^32=4GB,这样来看,段的长度是不是太短了?其实,在DES1中,有一位用于表示段长度的单位,当它被置1时(一般情况下都是如此),表示长度单位为4KB,这样,一个段的最大可能尺寸就成了1M*4K=4G,与地址空间相稳合。4KB也正是一个内存页的大小,说明段的大小也是向页对齐的。

另外,注意到一个有趣的现象吗?段描述项的结构被设计得不连续,不论是段基地址还是段长度,都被分成了两节表示。这样的设计与80286的过渡有关。上面的段描述项结构去掉第一行后剩下的三行正是286的段描述项。286被设计为24位地址总线,所以段基址是24位,相应地段长是16位。在386的地址总线扩展为32位之后,还必须兼容286产品的设计,所以只好在段描述项上“打补丁”。

在386中,段寄存器还是16位,那么16位的段寄存器如何存放得下64位的段描述项? 段描述项不再由段寄存器直接持有。段描述项存放在内存里,系统中可以有很多个段描述项,这些项连续存放,共同构成一张表,16位的段寄存器里只是含有这张表里的一个索引,但也并不仅是一个简单的序号,而是存储了一种数据结构,这种结构的定义如下:

index (b15 ~ b3)TI (b2)RPL (b1 ~ b0)

其中index是段描述表的索引,它指向其中的某一个段描述项。RPL表示权限,00最高,11最低。

还有一个关键的问题,内存中的段描述表的起始地址在哪里?显然光有索引是有不够的。为此,Intel又设计了两个新的寄存器:GDTR(global descriptor table register)和LDTR(local descriptor table register),分别用来存储段描述表的地址。段寄存器中的TI位正是用于指示使用GDTR还是LDTR。

当用户程序要求访问内存时,CPU根据指令的性质确定使用哪个段寄存器,转移指令中的地址在代码段,取数指令中的地址在数据段;根据段寄存器中的索引值,找到段描述项,取得段基址;指令中的地址是段内偏移,与段长比较,确保没有越界;检查权限;把段基址和偏移相加,构成物理地址,取得数据。

新的设计中处处有权限与范围的限制,用户程序只能访问被授权的内存空间,从而实现了保护机制。就这样,在段寄存器的基础上,Intel实现了自己的“保护模式”。

三、 与页式存管并存

现代操作系统的发展要求CPU支持页式存储管理。

页式存管本身是与段式存管分立的,两者没有什么关系。但对于Intel来说,同样是由于“段寄存器”这个历史的原因,它必须把页式存管建立在段式存管的基础之上,尽管这从设计的角度来说这是没有道理,也根本没有必要的。

在段式存管中,由程序发出的变量地址经映射(段基址+段内偏移)之后,得到的32位地址就是一个物理地址,是可以直接放到地址总线是去取数的。

在页式存管中,过程也是相似的,由程序发出的变量地址并不是实际的物理地址,而是一个三层的索引结构,这个地址经过一系统的映射之后才可以得到物理地址。

现在对于Intel CPU来说,以上两个映射过程就要先后各做一次。由程序发出的变量地址称为“逻辑地址”,先经过段式映射成为“线性地址”,线性地址再做为页式映射的输入,最后得到“物理地址”。

Linux内核实现了页式存储管理,而且并没有因为两层存管的映射而变得更复杂。Linux更关注页式内存管理,对于段式映射,采用了特殊的方式把它简化。让每个段寄存器都指向同一个段描述项,即只设了一个段,而这个段的基地址为0,段长度设为最大值4G,这个段就与整个物理内存相重合,逻辑地址经映射之后就与线性地址相同,从而把段式存管变成“透明”的。

这就是Intel处理器中“段寄存器”的故事。
<think>嗯,我现在要介绍一下8086段寄存器。首先,我得回忆一下8086处理器的基本结构。8086Intel早期的16位微处理器,它的寻址方式有什么特点呢?对了,它采用了分段内存管理,这样可以用16位的寄存器来访问更大的内存空间。 段寄存器应该是用来存储这些内存段的基地址的。常见的段寄存器有CS、DS、SS、ES这几个对吧?那每个段寄存器的作用可能不同。比如,CS是代码段寄存器,指向当前执行的代码段;DS是数据段寄存器,指向数据段;SS是堆栈段寄存器,用于堆栈操作;ES是附加段寄存器,可能在处理字符串等操作时使用额外的数据段。 不过具体的,每个段寄存器对应的用途需要准确描述。比如,CS:IP组合决定下一条要执行的指令地址,DS和ES用于数据访问,SS:SP或SS:BP用于堆栈操作。此外,段寄存器与偏移地址的组合形成20位的物理地址,计算方式是段寄存器左移4位加上偏移地址。比如,DS=0x1234,偏移地址0x5678,物理地址就是0x12340 + 0x5678 = 0x179B8。 可能还需要说明为什么需要分段管理,因为8086的地址总线是20位,而寄存器只有16位,所以通过分段机制可以访问1MB的内存空间。每个段最大64KB,因为偏移地址是16位的。这样,虽然每个段不超过64KB,但通过改变段寄存器的值,可以覆盖整个1MB内存。 另外,段寄存器的使用示例可能有帮助。比如,MOV指令中如何指定段寄存器覆盖默认的段。例如,MOV AX, [BX]默认使用DS,但可以用段前缀如MOV AX, ES:[BX]来使用ES段。 还需要注意段寄存器的限制,比如某些指令必须使用特定的段寄存器,如堆栈操作PUSH和POP必须使用SS,指令的取指必须使用CS等。 检查一下是否有错误的地方。例如,段寄存器存储的是段基址的高16位,左移4位后加上偏移得到20位地址。每个段寄存器对应的段类型是否正确。另外,是否有其他段寄存器,比如后来的80386有更多的段寄存器,但8086只有四个:CS、DS、SS、ES。 总结来说,段寄存器的作用、各个寄存器的用途、地址计算方式以及分段机制的原因都是需要涵盖的点。要确保语言准确,例子正确,没有技术错误。</think>8086处理器的段寄存器是其内存分段管理机制的核心组成部分,主要用于生成20位的物理地址,从而支持1MB内存空间的访问。以下是关键要点: ### 一、段寄存器的作用 1. **地址扩展** 8086为16位架构,但通过`段基址×16 + 偏移量`的计算方式(例:段寄存器值`0x1234`左移4位得`0x12340`,加上偏移`0x5678`,最终地址`0x179B8`),实现20位物理地址,突破16位寻址限制。 2. **分段管理** 将内存划分为代码、数据、堆栈等逻辑段,各段最大64KB(偏移地址16位)。 ### 二、4个段寄存器及用途 | 段寄存器 | 名称 | 用途 | |----------|---------------|----------------------------------------------------------------------| | **CS** | 代码段寄存器 | 指向当前执行的代码段,与**IP**寄存器组合确定下一条指令地址(CS:IP) | | **DS** | 数据段寄存器 | 指向默认数据段,用于大多数数据操作(如`MOV AX, [BX]`默认使用DS) | | **SS** | 堆栈段寄存器 | 管理堆栈区域,与**SP/BP**寄存器配合处理堆栈操作(如PUSH/POP指令) | | **ES** | 附加段寄存器 | 作为辅助数据段,常用于字符串操作(如`MOVSB`指令使用ES:DI目标地址) | ### 三、关键特性 1. **段覆盖前缀** 可通过显式指定段寄存器覆盖默认段,例如: ```assembly MOV AX, ES:[BX] ; 使用ES而非默认的DS ``` 2. **硬件强制关联** - 指令取指**必须**通过CS段 - 堆栈操作(PUSH/POP)**必须**使用SS段 3. **编程影响** 开发者需手动管理段寄存器,例如: ```assembly MOV AX, @DATA MOV DS, AX ; 初始化数据段 ``` ### 四、设计意义 - **兼容性与扩展性**:在16位寄存器基础上实现1MB内存访问,为后续x86架构奠定基础。 - **多任务支持雏形**:通过分段隔离代码、数据和堆栈,为后期保护模式提供演进路径。 ### 示例:物理地址计算 若`CS=0x0200`,`IP=0x0100`,则: 物理地址 = `0x0200 << 4 + 0x0100 = 0x2000 + 0x0100 = 0x2100` 通过这种机制,8086在硬件资源有限的情况下,高效地扩展了内存寻址能力。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

JXES智能生态系统

如文章对你有用,请作者喝个咖啡

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值