(二)CPU和内存是怎么配合的

1,280 阅读9分钟
  • CPU(Central Processing Unit)。

计算机的工作模式

计算机工作模式.png

  • CPU是计算机最核心的硬件,它做了计算,它需要和其他硬件配合起来工作。

  • 图中能看到CPU与其他硬件是通过总线(Bus)连接起来的。它们之间的数据传递依靠的都是总线。

  • 其他硬件中最重要的就是内存(Memory)。内存是用来存储指令和被指令操作的数据的。

    • 指令分为两部分:

      • 具体的操作,比如加法、减法等。
      • 需要参与操作的数据。

CPU和内存是如何配合的

  • 首先看CPU的组成,它分为三个部分:

    • 运算单元

      • 只管指令的运算部分,如加减法,位移等运算。但是它并不知道要运算哪些数据,也不知道计算的结果需要存在哪里。
    • 数据单元

      • 在描述内存的基本作用时,讲到数据存储在内存中,但是如果运算单元每次计算时都需要通过总线从内存中读取数据,那么整体的处理速度就太慢。因为内存处理速度比起CPU慢很多,而CPU又不得不等它。如果能将一部分需要经常访问的数据,可以直接存储在CPU就好了。这是数据单元,它虽然空间不大,但是速度飞快。数据单元包括CPU的缓存和寄存器组。
    • 控制单元

      • 控制单元是一个指挥中心,它可以不断获取下一条要执行的指令,然后执行。这条指令会指导运算单元从数据单元取出数据,计算结果,然后存到数据单元中。
  • 而数据单元的存储空间非常有限,没办法保存太多的运算过程中产生的中间结果,内存来存储这些中间结果的。

  • 配合图示:

cpu与内存的合作.png

  • 编写的程序是放在硬盘上的,但程序执行时,躺着的代码就变成了活着的进程。在内存中就会有对应的空间。

  • 图中有A,B两个进程。在内存中就有两个独立的内存空间,各自加载了自己的程序。进程的内存空间可以简单的理解成分为代码段和数据段两个部分。

  • 每一个进程的内存空间是相互隔离的,但是并不一定是连续的,有点链表的意思。

  • 控制单元听上去是最神奇的,它是如何知道当前需要执行哪一条指令的呢?

    • 它有一个指令指针寄存器,里面存放着下一条需要执行的指令的地址(在当前的进程的内存空间中)。

    • 控制单元会不断地从指令指针寄存器中拿到指令在内存的地址,并读到指令,然后写入它的指令寄存器。

    • 当指令寄存器中有指令时,控制单元就会把指令的计算部分交给运算单元,数据部分交给数据单元。

      • 数据单元会根据数据的地址,从内存空间中的数据段读数据保存在数据寄存器中。
      • 完成运算后的结果会先存在数据寄存器中。最终会有指令将这些数据又写回内存空间中。
      • 所以图中关于数据的箭头是双向的。
    • 整个控制单元的工作流程,只要保证指令指针寄存器中会有值更新,那么进程就能一步步运行下去。指令指针寄存器的值是如何更新的,之后的文章会讨论到。

  • 当一个CPU同一时间段要同时处理多个进程时,是如何切换进程的呢?

    • CPU有两个寄存器,一个存着当前处理进程的代码段起始位置(指令起始地址寄存器),另一个存储当前处理进程的数据段起始位置(数据起始地址寄存器)。
    • 当从A进程切换到B进程的时候,就是替换这两个寄存器的值。
    • 当然进程切换还有会非常多的复杂操作,之后再详细分析。
  • 图中内存部分和CPU部分之间的实线箭头就是总线。CPU和内存之间的数据传输就是靠的总线。

    • 传输数据的叫数据总线。

    • 传输地址的叫地址总线。

    • 总线的位数就决定了传输的能力。

      • 比如地址总线只有2位。那CPU就只能读到内存中的4个位置:00,01,10,11。
      • 比如数据总线只有2位,那一次传输就只能2位。如果需要传输一个8位的数,就需要4次。数据总线的位数决定了传输速度。

X86

  • X86是硬件平台,该平台是开放、统一、兼容的。
  • 操作系统是使用硬件的。如果硬件平台不兼容,那操作系统就很难统一起来。
  • 这个系列开端于8086处理器,因此称为x86架构。
  • 8086处理器,数据总线宽16,地址总线宽20,所以寻址空间是 2^20 = 1M

8086

8086cpu组件.png

数据单元

  • 对应图中浅橙黄色部分。

  • 为了暂存数据,设置了8个16位的通用寄存器,主要用来在计算过程中暂存数据

    • AX,BX,CX,DX,SP,BP,SI,DI
    • 其中AX,BX,CX,DX分为两个8位的寄存器来使用: AH,AL,BH,BL,CH,CL,DH,DL

控制单元

  • 对应图中绿色部分

  • IP寄存器:指令指针寄存器(Instruction Pointer Register)

    • CPU会不断的从IP寄存器中读取指令地址

    • 并将指令从内存的段中加载到CPU的指令队列中

    • 然后交给运算单元去执行指令

    • 标记个问题以后再写

      • 谁往IP寄存器里面写东西?
  • CS、DS、SS、ES四个16位的段寄存器,是用来做进程切换的。

    • CS,代码段寄存器(Code Segment Register)
    • DS,数据段寄存器(Data Segment Register)
    • SS,栈寄存器(Stack Register)
    • ES,附加段寄存器(Extra Segment)
如何在内存中找指令和数据

CS、DS有什么用,要先了解从内存中找指令和数据的机制

  • CS和DS存的是目前正在执行进程段(分别对应代码段和数据段)的起始地址
  • 代码段的偏移量存在IP寄存器
  • 数据段的偏移量存在通用寄存器
  • 代码和数据的真实地址 = 起始地址 + 偏移量

存在一个问题

  • 8086处理器,地址总线宽20。CS、DS是16位的,IP寄存器和通用寄存器也是16位的

  • 那怎么通过20位宽的地址总线传递16位的地址?

    • 起始地址 * 16 + 偏移量

      • *16的意思就是左移4位,相当于在原有地址后面加了4个0
      • 这样虽然内存有1M,但是能作为起始地址的只能是结尾为4个0的地方,也就是能被16整除的地址位
  • 由于偏移量只能是16位的(IP寄存器是16位的),所以一个段的大小只能是2^16 = 64k

所以切换进程就是切换了CS和DS中的起始地址

函数调用

凡是与函数调用相关的操作,都与栈紧密相关。SS就是一个栈寄存器。

ss原理示意.png

  • A函数先执行完语句1,就开始调用B函数了,为了将来执行完B之后,还能记得当前执行到了A函数的哪一行,所以要将A的基本信息入栈。然后开始执行B函数的语句3,执行完后,调用了C,同理也需要将B的基本信息入栈。执行语句5之后,从SS中弹出了B的基本信息,就能找到当年B调用C之前执行到了哪一行代码,然后继续往后语句4。之后同理弹出A的基本信息,找到语句2的位置。执行完毕。

32位处理器

32位处理器,地址总线变成了32位,这样能访问到的内存就是2^32 = 4G。如果继续使用8086的寄存器机制,就浪费内存了。

32位总线的寄存器.png

数据单元

  • 数据单元的寄存器,由原来的16位变成了32位。这是完全兼容了8086的那种处理方式。

控制单元

  • IP寄存器扩展成了32位

  • 变化最大的就是原有的段寄存器

    • 8086的地址总线是20位的,所以用原本的起始位置 * 16来表示起始位置
    • 那这样子,内存中的起始位置只能是能整除16的地方
  • 这里重新定义了段寄存器

    • CS、DS、SS、ES仍然是16位,但是这里不再存储真正的段起始地址
    • 真正的段起始地址,存在内存中的某一个地方,这地方是一个表格,表格中的一项一项是段描述符(Segment Descriptor),这里面才是真正的起始地址。
    • 段寄存器里面保存的哪一项,称为选择子(Selector)。
    • 这样拿段起始地址的过程就变成了,先从段寄存器中找到表格中的一项(选择子),然后再从表格中的一项拿到真正的段起始地址(段描述符)。
    • 为了更快的拿到段起始地址,段寄存器会从内存中的段描述符,拿到CPU的高速缓存器中。

数据单元兼容了8086,但是控制单元中的IP寄存器兼容,但段寄存器没有兼容,于是有了实模式和保护模式

实模式与保护模式

  • 段寄存器中直接保存真实起始地址的模式叫实模式
  • 段寄存器中保存段描述符的模式叫保护模式
  • 可以通过切换模式来兼容

总结

寄存器总结.png

  • 最后放一张升级版的CPU是怎么和内存配合的示意图。