第二回-x86分段机制

       接着第一回继续往下说,x86计算机上电之初运行在16位的实模式,并且MBR被加载到内存地址0x7C00时,此时CPU的CS与IP寄存器值分别是多少呢?应该有不少资料已经讲过实模式下的内存地址为段寄存器左移4位(等同于乘以6)加上段内偏移,因此大家也能计算出CS为0x07C0,IP为0x0。但是你知道为什么x86访问会采用这样分段的方式,这样有什么好处呢?当CPU发展到32位或64位,分段机制还存在吗?

      这个就要给大家讲故事了,Long long Ago....。x86 CPU设计之时就限定了CPU能直接从寄存器中获取数据,即使是内存中的数据也要先从内存中读取到相应的寄存器CPU才能访问,因此16位 CPU就有了段寄存器(DS、CS、ES、FS)以及通用寄存器AX、BX、CX、DX等。到了32位CPU时通用寄存器扩展了空间,数量也相应增加了,名称也发生变化,大家可以查询相关书籍。x86系列CPU 8086发布时CPU寄存器位数是16位,而此时8086的地址总线有20位,可以访问1M的地址空间。这样的处理器位数与地址总线的位数之间就发生冲突。16位二进制最大能表示多少呢?计算一下是64K,所以CPU在传输地址时一次只能传输16位的有效地址。为了访问大于64K的内存,我们可以想象一下,如何能办到呢?聪明的处理器设计者将内存逻辑上按段划分,每段内存最大64K。这样想要访问整个内存可以使用2个寄存器帮助完成:一个寄存器表示每段内存的基地址,一个寄存器表示在每段内存中的偏移量。这个思想就和TCP协议中的“滑动窗口”类似,大家可以想想是不是这个逻辑。因此,CPU访问某一内存时,发送到地址总线上的地址值为:段基址寄存器的值左移4位加上偏移寄存器的值,以“段及地址:段内偏移”方式定义,其实这就是大家平时所熟知的逻辑地址。由逻辑地址到物理地址(即地址总线上的真实值)的计算实现是由CPU硬件电路实现,也就是CPU会自动进行该项计算,不需要程序控制。如下图所示,CPU要访问并执行MBR程序,那就需要将MBR包含在某一段内存中,如果设置代码段寄存器CS为0x07C0,整个MBR才512字节,肯定在以CS为段基地址的64K内存段中。这里代码段寄存器CS能设置为其它值吗?答案是肯定的,只要这个内存段能包含0x7C00往上的512字节即可,如以0x0作为段基地址,最大内存地址为0xFFFF的内存段。但是又引入了另外一个问题,”操作系统灵魂拷问“中提及到过

该问题,任意内存地址都能作为段基地址吗?答案是否定的。根据前面的描述段基地址是寄存器CS左移4位形成,所以段基地址的最低4位必须是0。换一个思维思考,如果最低4位不是0,原段基地址转换成段寄存器里存放内容时会向右移动4位。这会是一个什么结果,最右的4位数据会丢失,这个好理解吧,所以不是每个内存地址都能用作段基地址。

  8086采用分段机制另外一个作用是解决程序在内存中的重定位问题。有了逻辑地址的概念后,程序编译过程中分配的地址为相对与段基地址的偏移,不再是物理地址。这样可以有一个很大的好处,编写的程序无论加载到内存的什么位置都可以在不改变程序的情况下可以运行。为什么这样说呢?思考一下,程序加载到内存某个段,就将CS寄存器的值设置为该内存段的段基地址。程序中的访问是按段内偏移来寻址,段内偏移是一直保持不变的,只要CS设置成程序加载到内存段的段基地址,CPU的分段机制都可以访问到正确的程序地址。所以具体加载到哪个段,对程序内部寻址是透明,程序本身是感受不到段的存在,所有的这一切都通过分段机制完美的过滤掉。这里描述的有点啰嗦,可以参考下图,MBR程序编译后不需要任何改变就可加载内存中任意内存段。

 虽然CPU的寄存器分了什么代码段寄存器、数据段寄存器等,其实CPU不是那么智能,它不认识内存里的是到底数据还是程序代码,它执行只认寄存器。如果你把CS设置为一段数据的内存基址,CPU会把这段数据当做程序指令去翻译指令、执行指令等过程,只不过此时CPU会飞到天涯海角,不知所踪了。

      随便x86 CPU的发展,8086也进化到了32位,甚至64位。32位的x86 CPU是一个划时代的分界点,它提供一种成为保护模式的运行方式,此模式下内存寻址也发生了变化,引入了虚拟地址。虚拟地址的管理随着本系列故事的推进会在后面详细说。Intel的x86能够成为芯片厂商Top1是有原因的,它的每一代新CPU都号称能够较好的兼容老CPU,我认为这是它赢得宝座的法宝之一。所以8086的分段机制也是保留下来的,即使分页机制的出现,分段机制也绕不过去。那32位x86 CPU保护模式下是如何利用分段机制访问内存的呢?答案是段描述表。32位x86 CPU继续使用段寄存器DS、CS、ES、FS等,只不过这些寄存器都是16位,根本存放不下32位的地址值。因此32位保护模式下的段寄存器用于存放段描述符选择子,段描述符选择子主要是用于在段描述表中索引具体的段描述,段描述中有内存段的段基地址、内存段的长度,以及其它一些重要属性。本回只描述一个总体流程,具体的细节会在下一回保护模式继续讨论。

     操作系统在启动期间会建立相应的段描述表,并将段描述表基址加载到寄存器GDTR。本回以访问段基地址0x20000,长度为16M的内存段为例来讲述,如上图所示。CPU需要访问该内存段中的代码时,第一步,访问寄存器GDTR得到段描述表基址;第二步,访问寄存器CS的代码段选择子,进而得到待访问内存段端描述符在段描述表中的索引。由于一个段描述符为8字节,所以访问第N个描述,段描述符在段描述符表中的偏移为:段描述符索引*8。这样与第一步得到的段描述表基址相加便得到段描述符的地址;第三步,由段描述符可得到待访问内存段基址。段基地址加上段内偏移(IP)即可访问该内存段内的任意内存地址。当然这还涉及到段保护措施,也就是所谓的保护模式。随着计算机的发展,内存管理还出现了平坦模型,这些都会在后面会慢慢道来。

       砍柴不误磨刀功,大家可能觉得还没有进入操作系统实现环节。正如第一回所说,操作系统是处理器硬件的应用程序,我们需要把影响操作系统运行的一些重要硬件机制说明白才能更好的设计并实现操作系统。

      写本回的时候正好是2022年的春节前夕,祝大家2022年虎虎生威、财源滚滚!

======================================================================

各位看官如果对本系列有兴趣,大家一起学习交流,可以加一下该系列微信公众号,鼓励继续写的动力。声明此公众号是个人申请用于交流学习底层技术,不会涉及其他商业行为。

微信公众号链接: https://mp.weixin.qq.com/s/IXrszNBuf2pDxJRIHMvOpQ

======================================================================

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值