Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

第一次呼吸

复习

  1. 第三十五章至第三十八章:定义了指令,做出了控制器和 CPU
  2. 第三十九章:了解了指令流水线
  3. 第四十章至第四十二章:接上了总线、输入输出和中断

TL;DR

  • 这一章把最后的所有系统合到一起,让一台完整的计算机跑起来
  • 我们放一段程序进内存,看它从取指到输出,全程自动完成
  • 至此,从晶体管到整机,这台机器第一次“活“了过来

正文

  零件的时代结束了。运算器、存储器、CPU、总线、外设、中断,全都造好了。今天,我们让它们一起呼吸。

全家福

        ┌──────────────────────────────────────┐
        │              CPU                     │
        │  ┌──────────┐      ┌────────────┐    │
        │  │  控制器   │◀────▶│   运算器    │    │
        │  └──────────┘      └────────────┘    │
        │        ▲   ┌──────────────────┐      │
        │        └───│ 寄存器 / PC / IR  │      │
        │            └──────────────────┘      │
        └────────────────┬─────────────────────┘
                         │
                    ┌────┴────┐
                    │  总线   │
                    └────┬────┘
          ┌──────────────┼──────────────┐
          ▼              ▼              ▼
      ┌────────┐    ┌─────────┐    ┌──────────┐
      │  内存   │    │ 缓存     │    │ 输入输出  │
      │(存储器) │    └─────────┘    │  设备     │
      └────────┘                    └──────────┘

  这张图里的每一个方块,都对应前面某一章。它们不再是孤立的零件,而是一个系统。

放一段程序进去

  我们在内存里放一段很小的程序,它做一件事:把两个数加起来,再把结果输出到一个灯泡上。

地址     指令
100     LOAD R1, [200]   ; 把内存 200 处的数取到 R1
104     LOAD R2, [201]   ; 把内存 201 处的数取到 R2
108     ADD  R3, R1, R2  ; R3 = R1 + R2
112     STORE [202], R3  ; 把 R3 写回内存 202
116     OUT  [202]       ; 把内存 202 的内容输出到灯泡
...
200     3
201     5
202     0

看它自己跑起来

  接下来,没有任何人干预。时钟开始跳动,CPU 开始工作:

第 1 个周期   取指令:PC=100,把 "LOAD R1,[200]" 取进 IR
第 2 个周期   译码:控制器认出这是一条"加载"指令
             执行:从内存 200 取出 3,送进 R1
第 3~5 周期   同理,R2 取到 5
第 6 个周期   取指、译码 "ADD R3,R1,R2"
             执行:运算器算出 8,写进 R3
第 7 个周期   把 8 写回内存 202
第 8 个周期   取指、译码 "OUT [202]"
             执行:通过总线,把 8 送到输出设备

  灯泡亮了——它表示数字 8。

  整个过程,没有一只手拨开关,没有一个数被人抄写。机器自己取指令、自己译码、自己运算、自己输出。它活了。

它还会被打断

  就在这时,键盘(一个输入设备)被按下了。它拉响中断信号。CPU 不慌不忙:先把当前的程序现场保存下来,去处理键盘中断,处理完再恢复现场,从刚才断掉的地方继续。程序仿佛什么都没发生过。

  这就是第四十二章的中断系统在起作用。机器不仅能自动跑,还能同时应付外界的突发状况

回过头看

  从第一章“因为懒,想要一台会算数的机器“,到现在,我们亲手走过了:

晶体管 → 逻辑门 → 加法器 → 运算器
       → 锁存器 → 触发器 → 寄存器 → 存储器
       → 指令 → 控制器 → CPU → 总线 → 外设 → 中断
       → 一台会呼吸的计算机

  这就是计算机组成原理的全貌。它不是一堆孤立零件的堆砌,而是一层层搭起来、彼此咬合的整体。

  再回头看总览里那四句话,现在应该句句都能体会了:

  • 懒是人类进步的第一动力——整台机器,就是“想把活儿交给机器“的产物
  • 没有什么问题是加一层解决不了的——从门到 ALU,从寄存器到缓存,处处如此
  • 人类所有知识都来源于好奇心和解决问题——每个部件,都是被一个具体问题逼出来的
  • 所有庞大学科知识体量均由积累而成——四十多章,也是一小步一小步垒起来的

  到这里,组成原理这条线就走完了。但计算机科学远不止于此,远处还有更辽阔的世界:

  • 操作系统:怎样管理 CPU、内存、文件和设备
  • 编译原理:高级语言如何一步步变成机器能懂的 0 和 1
  • 计算机网络:多台计算机怎样连成一张网
  • 数据结构与算法:怎样把问题解得更快更好

  本站的《计算机科学极简入门指南》还会有更多分册,欢迎继续同行。

  如果你想亲手把这些电路搭出来,可以试试 Logisim 这类数字电路模拟器,或者直接玩一遍 nand2tetris 项目——你会发现,这一路学的东西,都能亲手拼一遍。

思考题

  在上面那段程序里,如果内存里存的不是指令,而是一串普通数据,CPU 会怎么样?冯·诺依曼体系结构“把程序和数据处理成一样“的优点和风险,分别是什么?

小结

知识点

  • CPU、内存、总线、外设、中断如何协同
  • 一条程序从取指到输出的完整流程
  • 中断如何在程序运行中插入处理
  • 从晶体管到整机的完整脉络

参考资料

  1. Wikipedia(zh):冯·诺依曼结构:存储程序思想
  2. Wikipedia(zh):指令周期:CPU 执行指令的过程

思考题答案(仅供参考)

  CPU 并不知道哪块内存是“指令“、哪块是“数据“,它只会照着 PC 指的地址去取、去译码、去执行。如果那里正好是数据,它也会当成指令去执行,结果往往是一团乱麻。

  好处是极大的灵活性:程序可以像数据一样被加载、修改、复制,一台通用机器能跑无数种程序。风险也在于此:一旦指针出错、程序跳到数据区,或者恶意代码把数据伪装成指令,机器就会执行本不该执行的东西——缓冲区溢出攻击就是利用这一点。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪