物理地址
我们大家都知道, CPU 访问内存时,需要知道访问内存的具体地址,内存单元是内存的基本单位,每一个内存单元在内存中都有唯一的地址,这个地址即是 物理地址
。而 CPU 和内存之间的交互有三条总线,即数据总线、控制总线和地址总线。
CPU 通过地址总线将物理地址送入存储器,那么 CPU 是如何形成的物理地址呢?这将是我们接下来的讨论重点。
现在,我们先来讨论一下和 8086 CPU 有关的结构问题。
cxuan 和你聊了这么久,你应该知道 8086 CPU 是 16 位的 CPU 了,那么,什么是 16 位的 CPU 呢?
你可能大致听过这个回答,16 位 CPU 指的是 CPU 一次能处理的数据是 16 位的,能回答这个问题代表你的底层还不错,但是不够全面,其实,16 位的 CPU 指的是
- CPU 内部的运算器一次最多能处理 16 位的数据
❝运算器其实就是 ALU,运算控制单元,它是 CPU 内部的三大核心器件之一,主要负责数据的运算。
❞
- 寄存器的最大宽度为 16 位
❝这个寄存器的最大宽度值就是通用寄存器能处理的二进制数的最大位数
❞
- 寄存器和运算器之间的通路为 16 位
❝这个指的是寄存器和运算器之间的总线,一次能传输 16 位的数据
❞
好了,现在你应该知道为什么叫做 16 位 CPU 了吧。
在你知道上面这个问题的答案之后,我们下面就来聊一聊如何计算物理地址。
8086 CPU 有 20 位地址总线,每一条总线都可以传输一位的地址,所以 8086 CPU 可以传送 20 位地址,也就是说,8086 CPU 可以达到 2^20 次幂的寻址能力,也就是 1MB。8086 CPU 又是 16 位的结构,从 8086 CPU 的结构看,它只能传输 16 位的地址,也就是 2^16 次幂也就是 64 KB,那么它如何达到 1MB 的寻址能力呢?
原来,8086 CPU 的内部采用两个 16 位地址合成的方式来传输一个 20 位的物理地址,如下图所示
叙述一下上图描述的过程
CPU 相关组件提供两个地址:段地址和偏移地址,这两个地址都是 16 位的,他们经由地址加法器
变为 20 位的物理地址,这个地址即是输入输出控制电路传递给内存的物理地址,由此完成物理地址的转换。
地址加法器采用 「物理地址 = 段地址 * 16 + 偏移地址」 的方法用段地址和偏移地址合成物理地址。
下面是地址加法器的工作流程
其实段地址 * 16 ,就是左移 4 位。在上面的叙述中,物理地址 = 段地址 * 16 + 偏移地址,其实就是「基础地址 + 偏移地址 = 物理地址」 寻址模式的一种具体实现方案。基础地址其实就等于段地址 * 16。
你可能不太清楚 段
的概念,下面我们就来探讨一下。
什么是段
段这个概念经常出现在操作系统中,比如在内存管理中,操作系统会把不同的数据分成 段
来存储,比如 「代码段、数据段、bss 段、rodata 段」 等。
但是这些的划分并不是内存干的,cxuan 告诉你是谁干的,这其实是幕后 Boss CPU 搞的,内存当作了声讨的对象。
其实,内存没有进行分段,分段完全是由 CPU 搞的,上面聊过的通过基础地址 + 偏移地址 = 物理地址的方式给出内存单元的物理地址,使得我们可以分段管理 CPU。
如图所示
这是两个 16 KB 的程序分别被装载进内存的示意图,可以看到,这两个程序的段地址的大小都是 16380。
❝这里需要注意一点, 8086 CPU 段地址的计算方式是段地址 * 16,所以,16 位的寻址能力是 2^16 次方,所以一个段的长度是 64 KB。
❞
段寄存器
cxuan 在上面只是简单为你介绍了一下段寄存器的概念,介绍的有些浅,而且介绍段寄存器不介绍段也有「不知庐山真面目」的感觉,现在为你详细的介绍一下,相信看完上面的段的概念之后,段寄存器也是手到擒来。
我们在合成物理地址的那张图提到了 相关部件
的概念,这个相关部件其实就是段寄存器
,即 「CS、DS、SS、ES」 。8086 的 CPU 在访问内存时,由这四个寄存器提供内存单元的段地址。
CS 寄存器
要聊 CS 寄存器,那么 IP 寄存器是你绕不过去的曾经。CS 和 IP 都是 8086 CPU 非常重要的寄存器,它们指出了 CPU 当前需要读取指令的地址。
❝CS 的全称是 Code Segment,即代码寄存器;而 IP 的全称是 Instruction Pointer ,即指令指针。现在知道这两个为什么一起出现了吧!
❞
在 8086 CPU 中,由 CS:IP
指向的内容当作指令执行。如下图所示
说明一下上图
在 CPU 内部,由 CS、IP 提供段地址,由加法器负责转换为物理地址,输入输出控制电路负责输入/输出数据,指令缓冲器负责缓冲指令,指令执行器负责执行指令。在内存中有一段连续存储的区域,区域内部存储的是机器码、外面是地址和汇编指令。
上面这幅图的段地址和偏移地址分别是 2000 和 0000,当这两个地址进入地址加法器后,会由地址加法器负责将这两个地址转换为物理地址
然后地址加法器负责将指令输送到输入输出控制电路中
输入输出控制电路将 20 位的地址总线送到内存中。
然后取出对应的数据,也就是 「B8、23、01」,图中的 B8、BB 都是操作数。
控制输入/输出电路会将 B8 23 01 送入指令缓存器中。
此时这个指令就已经具备执行条件,此时 IP 也就是指令指针会自动增加。我们上面说到 IP 其实就是从 Code Segment 也就是 CS 处偏移的地址,也就是偏移地址。它会知道下一个需要读取指令的地址,如下图所示
在这之后,指令执行执行取出的 B8 23 01 这条指令。
然后下面再把 2000 和 0003 送到地址加法器中再进行后续指令的读取。后面的指令读取过程和我们上面探讨的如出一辙,这里 cxuan 就不再赘述啦。
通过对上面的描述,我们能总结一下 8086 CPU 的工作过程
- 段寄存器提供段地址和偏移地址给地址加法器
- 由地址加法器计算出物理地址通过输入输出控制电路将物理地址送到内存中
- 提取物理地址对应的指令,经由控制电路取回并送到指令缓存器中
- IP 继续指向下一条指令的地址,同时指令执行器执行指令缓冲器中的指令