【操作系统真象还原】第3章:完善MBR(3.2节 上)

目录

3.2 CPU 的实模式

3.2.1 CPU 的工作原理

3.2.2 实模式下的寄存器

3.2.3 实模式下内存分段由来

3.2.4 实模式下 CPU 内存寻址方式


3.2 CPU 的实模式

实模式是指 8086 CPU 的寻址方式,寄存器大小,指令用法等,是用来反映 CPU 在该环境下如何工作的概念。所以想了解实模式这种抽象的概念,主要就是了解在实模式下 CPU 能做什么。

3.2.1 CPU 的工作原理

CPU 的唯一的任务就是执行指令,在它眼里,指令就是一串 010101 ......

CPU 执行每条指令的流程:

  1. 控制单元要取下一条待运行的指令,该指令的地址在程序计数器 PC 中,在 x86CPU 上,程序计数器就是 cs:ip,于是读取 ip 寄存器后,将此地址送上地址总线,CPU 根据此地址便得到了指令,并将其存入到指令寄存器 IR 中;
  2. 这时候轮到指令译码器 ID 上场了,它根据指令格式检查指令寄存器中的指令,先确定操作码是什么,再检查操作数类型,若是在内存中,就将相应操作数从内存中取回放入自己的存储单元,若操作数是在寄存器中就直接用了,免了取操作数这一过程;
  3. 操作码有了,操作数也齐了,操作控制器 OC 给运算单元下令,开工,于是运算单元便真正开始执行指令了,ip 寄存器的值被加上当前指令的大小,于是 ip 又指向了下一条指令的地址;
  4. 接着控制单元又要取下一条指令了,流程回到了本段开头,CPU 便开始了日复一日的循环,由于 CPU 特别不容易坏,所以唯怡让它停下来的条件就是停电。

3.2.2 实模式下的寄存器

寄存器是一种物理存储元件,只不过它比一般的存储介质要快,能够跟上 CPU 的步伐,所以在 CPU 内部有好多这样的寄存器用来给 CPU 存取数据。

缓存也是一项非常伟大的发明,成功解决了速度不匹配设备之间的数据传输,并且在一般情况下, IO 是整个系统的瓶颈,缓存的出现,有效减少了低速 IO 设备的访问频率,从而大幅度地提升了速度。如果缓存中恰好有要的内容,这就称为命中 hit ,否则称为缺失 mis。

CPU 中的一级缓存 L1,二级缓存 L2,它们都是 SRAM,即静态随机访问存储器,是最快的存储器。SRAM 是用寄存器来存储数据的,这就是 SRAM 快的原因。而寄存器为什么快呢?原因是寄存器是使用触发器实现的,这也是一种存储电路,工作速度极快,是纳秒级别的,和 CPU 的速度是一个级别。

CPU 中的寄存器大致上分为两大类:

  • 一类是其内部使用的,对程序员不可见。 “是否可见”不是说寄存器是否能看得见,是指程序员是否能使用。
  • 另一类是对程序员可见的寄存器,我们进行汇编语言程序设计时,能够直接操作的就是这些寄存器,如段存器,通用寄存器。

在实模式下,默认用到的寄存器都是 16 位宽的。(因为所谓的实模式是相对后面出现的保护模式而言的,CPU 最开始的工作模式就是 8086 CPU 的工作模式,即寄存器都是 16 位宽的)

段寄存器:

段寄存器的作用就是指定一片内存的起始地址,故也称为段基址寄存器,尽管段基址在实模式下要乘以 16 ,在保护模式下只是个选择子(保护模式中会讲),但其作用就是指定一片内存的起始地址,而段内偏移地址,顾名思义,就是仅仅相对于此起始地址的偏移量,

由于要指定的是内存中的一段区域的起始地址,所以称之为段基址寄存器,也称段寄存器,无论是在实模式下,还是保护模式下,它们都是 16 位宽。(这是为了更好地兼容嘛?

代码段:简而言之就是把所有指令都连续排放在一起,形成了一个全部都是指令的区域,里面存储的是指令的操作码及寻址方式等。该区域可以在硬盘上的文件中,也可以是被加载后的内存中,总之是一段指令区域。它们内部都是紧凑挨着的,内容形式完全一样,只是存放的介质不一样而己。代码段寄存器 CS 就是用来指向内存中这段指令区域的起始地址

数据段:和代码段类似,只是这段区域中的内容不是指令,而是纯粹的数据,也就是说里面存储的是程序运行所需要的数据,属于指令的操作数。数据段寄存器 DS 便是用来指向此数据区域的起始地址

栈段:其概念只在内存中,硬盘文件中可真没有。一般的栈段是由操作系统分配指定的,所以是属于被加载到内存后才有的。栈段寄存器 SS 就是用来指向此区域的起始地址。

另外三个附加段寄存器是为了方便大家,在 16 位 CPU 中,只有一个附加段寄存器——ES。而 FS,GS 附加段寄存器是在 32 位 CPU 中增加的。我们使用的是 32 位 CPU,并不是说 32 位 CPU 在实模式下的 16 位环境中就不能用 FS,GS 寄存器,32 位的 CPU 兼容 16 位 CPU 的特性。(兼容就是除了有自己额外新增的特性,还包括所有前辈的特性,只强不弱)

IP 寄存器是不可见寄存器,CS 寄存器是可见寄存器。(也就是说,程序员可以操作 CS 寄存器,而不能操作 IP 寄存器,因此程序员无法直接改变程序计数器 PC 来直接改变 CPU 的执行地址,只能通过像 jmp 这样的指令来跳转。)

复习🍊:

x86体系的CPU中:程序计数器 PC 并不是单一的某种寄存器,它是一种寄存器组合,指的段寄存器 cs 和指令寄存器 ip;有专门改变执行流的指令,如 jmp、call、int、ret ,这些指令可以同时修 cs 和 ip,它们在硬件级别上实现了原子操作。

ARM体系的CPU中:程序计数器有个专门的寄存器,名字就叫 PC,想要改变程序流程,直接对该寄存器赋值便可,可以用 mov 指令来修改程序流。(这个 PC 不知道是怎么实现的)

指令在内存中,访问内存就要用 “段:段内偏移” 的形式,所以 CS 寄存器用来存代码段段基址,IP 寄存器用来存储代码段段内偏移地址,同 CS 寄存器一样都是 16 位宽。并不是这两个寄存器真的决定了 CPU 的航向,只是 CPU 的航向被存入了这两个寄存器之中。(在 x86 体系架构中,是内存就应该用 “段基址:段内偏移地址” 的机制来访问,是地址就该有地方存放)

在执行当前指令的同时,在不跨段的情况下,CPU 以“ 当前 IP 寄存器中的值+当前执行指令的机器码长度” 的和作为新的代码段内偏移地址,将其存入 IP 寄存器,再到该新地址处读取指令井执行。 如果下一条指令需要跨段访问,还要加载新的段基址到 CS 寄存器。

flags 寄存器是计算机的窗口,展示了 CPU 内部各项设置、指标,任何一个指令的执行,其执行过程的细节,对计算机造成了哪些影响,都在 flags 寄存器中通过一些标志位反映出来。

通用寄存器:

无论是实模式,还是保护模式,通用寄存器都是有 8 个,分别是 AX BX CX DX SI DI BP SP。

拿 AX 寄存器举例,根据图 3-6 可知, AX 寄存器是由 AH 寄存器和 AL 寄存器组成的,它们都是 8 位寄存器。由于某种原因,16 位 AX 寄存器不够用了,将其扩展 (Extend) 为 32 位,在 AX 原有的基础上,增加 16 位作为高 16 位便是扩展的 AX,即 EAX。所以 EAX 归根结底也是由 AL,AH 组成的,AL,AH 值变了会直接影响 EAX。

通用是说每个寄存器的功能不单一,可以有多种用途,不像段寄存器 SS 那样只能用来放栈段基址,通用寄存器可以用来保存任何数据。虽说通用,但还是约定了它们的惯用法,除了通用的用途外每个寄存器肩负特定的功用。比如一般情况下,cx 寄存器用作循环的次数控制,bx 寄存器用于存储起始地址。这样,在为一些通用的函数传递参数时会方便很多;另外,一些指令已经固定用一些特定的寄存器作为参数了。

各通用寄存器特定的功能描述如下:

3.2.3 实模式下内存分段由来

CPU 中本来是没有实模式这一称呼的,是因为有了保护模式后,为了与老的模式区别开来,所以称老的模式为实模式。

实模式的 “实” 体现在:程序中用到的地址都是真实的物理地址,"段基址:段内偏移 " 产生的逻辑地址就是物理地址,也就是程序员看到的完全是真实的内存。

本节🍊:任何事物发展到今天,都有一段 “合理” 的过程,了解这一过程是怎么来的,有助于理解它今天的形态。

8086 之前的 CPU 对内存的访问比 8086 还要“实诚”,它们没有段的概念,程序中要访问内存,需要把地址写死,也就是所谓的 “硬编码”。由于 “硬编码” 的各种缺点,Intel 早期的工程师发明了“段”,即 CPU 访问内存用 “段+偏移” 的形式。它就是首次在 8086 上出现的,自那之后的 CPU 都是用这类思想访问内存,只是在形式上有小改动。为了支持段机制,CPU 中新增了段寄存器,如 cs 、ds、es 等。

查了一下为什么叫 8086🍊

这个起源可以从8085说起,这个名字的字面意思是:80年代生产的8位5伏电压处理器。后来,对这个进行升级,就简单的在个位上加1,变成了8086,也就出现了那个非常著名的x86鼻祖,于是就延用下来了。其实,后来的32位和64位系统,全名叫做x86-32 和 x86-64。但是,为了方便区分,就变成了x86和x64。

总结一下:8085是8位,8086是16位,x86-32是32位,x86-64是64位。其中,x86就是x86-32的简称,x64就是x86-64的简称。

8086 的地址总线是 20 位宽,也就是其寻址范围是 2^20 次方= IMB 。但其内部寄存器都是 16 位的,若用单一寄存器来寻址的话,只能访问到 2^16 次方等于 64KB 的空间。(地址线位宽和寄存器位宽是没有必然联系)

为了让 16 位的寄存器寻址能够访问 20 位的地址空间 (注意,这里说的是通过寄存器寻址,因为只有通过 16 位的寄存器去寻址才会受到 16 位的限制,立即数寻址不会受到限制),通过先把 16 位的段基址左移 4 位后变成 20 位,再加段内偏移地址,这样便形成了 20 位地址。

段基址为 0xFFFF0,偏移地址应该小于等于 F 就对啦,而这个偏移地址却是 0xFFFF,超出了 0xFFF0 的空间,也就是多出来 64K-16 字节,这部分内存就是传说中的高端内存区( High Memory Area, HMA )。这部分内存是不存在的,也不用处理,8086 只能接收 20 位长的地址(A0~A19 地址线),所以由于超过了 20 位而产生的进位,就会给丢掉,其作用相当于把地址对 IMB 取模了。这是地址回卷的效果,即超过最大范围后,从 0 重新开始计数。

3.2.4 实模式下 CPU 内存寻址方式

🍊其实一切所谓的格式都是一种“协议”、“约定”,“约定” 的原因是为了省事,让大家共同按照某种约定行事,这样服务的提供方就不必为满足各种各样需求方而煞费苦心。

为了 CPU 设计更容易,CPU 访问数据的形式也需要提前“约定”好,这就是所谓的寻址方式。8086 的寻址方式,从大方向来看可以分为三大类:寄存器寻址、立即数寻址、内存寻址。在内存寻址中又分为:直接寻址、基址寻址、变址寻址、基址变址寻址。

从名字上看,寻址就是寻找地址,寻找谁的地址?CPU 眼里只有二进制数,所以这是 CPU 在寻找“数” 的地址。这个“数”可以源操作数,也可以是目的操作数 (顺便提一句,Intel 汇编语言语法是“指令目的操作数,源操作数”。简而言之,寻址就是找到“数”的所在地,从哪来,往哪去

寄存器寻址:

最直接的寻址方式就是寄存器寻址,它是指“数”在寄存器中,直接从寄存器中拿数据就行了。只要牵扯到寄存器的操作,无论其是源操作数,还是目的操作数,都是寄存器寻址。若指令中有源操作数是立即数,也属于立即数寻址。

立即数寻址:

立即数就是常数。操作数 “直接” 存在指令中,直接拿过来,立即就能用了,为了突显“立即就能用”的高效率,此数便称为立即数,CPU 免去了找数的过程。

内存寻址:

操作数在内存中的寻址方式称为内存寻址。由于访问内存是用 “段基址:偏内偏移地址” 的形式,特别强调一下,此形式只用在内存访问中。默认情况下数据段寄存器是 DS,即段基址已经有了,只要再给出段内偏移地址就可以访问内存了,最终起决定作用的、有效的是段内偏移地址,所以段内偏移地址称为有效地址

直接寻址:直接寻址,就是将直接在操作数中给出的数字作为内存地址,通过中括号的形式告诉 CPU,取此地址中的值作为操作数。这里给出的数字是段内偏移地址,默认的段地址是 DS,若使用了段跨越前缀,那么段基址就在该前缀的段寄存器中。(书中有例子,更容易理解)

注⚠️:立即数寻址中的数字是直接拿来就用作操作数了,直接寻址中的数字是用来进一步寻址的。

基址寻址:就是在操作数中用 bx 寄存器或 bp 寄存器作为地址的起始,地址的变化以它为基础。在实模式下,限制了只能用 bx,bp 作为基址寄存器,bx 寄存器的默认段寄存器是 DS,而 bp 寄存器的默认段寄存器是 SS。(bp、sp 都是栈的有效地址,有效地址就是指偏移地址)。

实模式下,CPU 字长是 16(字长反映 CPU 一次运算的数字位数,而实模式下都是 16 位的数字),而栈的操作是以字长为单位的,因此实模式下的 push 指令默认情况下是压入 2 字节的数据,pop 指令默认是弹出 2 字节的数据。

其工作原理都可分为两步:(栈是向低地址方向发展的)

假如执行 push ax:

1 sub sp, 2                   先将 sp 中的值减去2

2 mov [sp], ax              再将 ax 中的值 mov 到新的 ss:sp 指向的内存

假如执行 pop ax:

1 mov ax, [sp]              先将 ss:sp 指向的值 mov 到 ax

2 add sp, 2                  再将 sp 的指针 +2

访问栈有两种方式,一种是把栈当成“栈”来使用,也就是用 push,pop 指令操作栈,但这样我们只能访问到栈顶,即 sp 指向的地址,没有办法直接访问到栈底和栈顶之间的数据。

很多时候,我们需要读写栈中的数据,即需要把栈当成普通数据段那样访问。处理器为了让开发人员方便控制栈中数据,提供了这种把栈当成数据段来访问的方式,可以用寄存器 bp 来给出栈中偏 移量,所以 bp 默认的段寄存器就是 SS,这样就可通过 SS: bp 的方式把栈当成普通的数据段来访问了。

变址寻址:变址寻址其实和基址寻址类似,只是寄存器由 bx,bp 换成了 si,di。si 是指源索引寄存器 (source index),di 是指目的索引寄存器 (destination index),两个寄存器的默认段寄存器也是 ds。变址寻址主要是用于字符搬运方面的指令,这两个寄存器在很多指令中都要成对使用。(配合基址寻址,用来实现基址变址寻址)

基址变址寻址:从名字上看,这是基址寻址和变址寻址的结合,即基址寄存器 bx 或 bp 加一个变址寄存器 si 或 di。

一种寻址方式对应一种电路实现,增加一种寻址方式,会增加硬件电路的复杂性,所以寻址方式是有限的。

🍊上层形式万千的变化,归根结底就是这么几类硬件电路,而它是有限的。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值