故事起源

  • 先驱想创造一个能执行程序的计算机,让我们来帮助他

开始构思 !

  • 我们知道,程序是由(代码 & 数据)组成的
  • 比如:假设我们想要计算1+2+3+...+100,那么我们不费吹灰之力就能写一个程序来完成这件事
  • 不难理解:
    1. 数据是程序处理的对象
    2. 代码描述了程序是如何对数据进行处理的

ok,既然我们想让计算机执行程序,那应该将程序放在哪儿呢 ?

  • 程序有几十行代码的“小程序”,也有上万行代码的“大程序”(比如大型游戏程序)
  • 显然,我们应该将程序放到一个拥有较大存储空间的“容器”中,这个容器叫:存储器
  • 于是,存储器诞生了,程序可被放入存储器中,等待着被 CPU 执行

等等,啥是 CPU ?

  • CPU,中文名叫中央处理器,听起来牛逼吧 ?!
  • 嗯,它确实牛逼,它是负责处理数据的核心电路单元,程序执行全靠它
  • 还不懂 ?
    • 你想啊,一个计算机它如果只有存储器,那还是不能计算嘛
    • 所以就得 CPU 扛起计算的重任喽
    • 咋扛呢 ?傻瓜,给 CPU 造运算器啊,然后就能对数据进行各种处理了
    • 啥 ?你觉得运算器太复杂 ?
    • 确实有点复杂哈,那就先只思考加法器吧~

  • 仍然以计算1+2+3+...+100为例
  • 对于本例,程序的编写方式是(将“部分”累加到“sum”上)

这就说明:有时候程序是需要对同一个数据进行连续的处理的

  • 那么现在硬件视角的执行是下面这样的

    • 从存储器读出 sum 和 1,传入 CPU,1 被加到 sum 上,将 sum 写回存储器
    • 从存储器读出 sum 和 2,传入 CPU,2 被加到 sum 上,将 sum 写回存储器
    • 从存储器读出 sum 和 3,传入 CPU,3 被加到 sum 上,将 sum 写回存储器
  • 啊西巴 !

    • 上面这样我们每完成一次累加都得将 sum 写回存储器,然后立刻就又将 sum 读出来继续加,这样太傻了啊
  • 为什么说上面这种方式很傻呢 ?你可以想像这样的场景:

    • 考试的时候你做一道题就向你同桌借一次橡皮,上道题做完你立马就还回去,下道题还借…
    • 懂了吧 ?!
  • 另外,大容量的存储器的读写速度是相对较慢的,这是谁都无法违背的材料特性规律

  • 于是先驱创造了小容量但高速的寄存器,CPU 正在处理中的数据可被暂放其中

至此,我们就有了(存储器、CPU、寄存器)

image-20220410180257453


  • 为了让强大的 CPU 成为我们忠实的奴仆,指令诞生了

  • 我们可以用指令来控制 CPU,让 CPU 做我们想做的事

  • 但(发一条指令,CPU 就动一动)的方式还是不尽人意,于是先驱就和 CPU 做了一个简单的约定

    • 给你一段程序,你执行完一条指令之后,就继续执行下一条指令
    • 可 CPU 怎么知道现在执行到哪一条指令呢 ?
    • 为此,先驱为 CPU 创造了程序计数器(Program Counter,PC)
    • 在 x86 中,它有一个特殊的名字,叫(Extended Instruction Pointer,EIP)
    • 从此,计算机就只需做一件事
    1
    2
    3
    4
    5
    while (1) {
    从 PC 指示的存储器位置取出指令;
    执行指令;
    更新 PC;
    }

至此,我们就设计出了一个足够简单的计算机

只需将一段指令序列放入存储器,然后让 PC 指向第一条指令,这段指令序列就会被计算机自动执行~

例如,下面的指令序列可以计算1+2+...+100

其中r1r2是两个寄存器,还有一个隐含的程序计数器PC,初值为0

为了帮助大家理解, 我们把指令的语义翻译成 C 代码放在右侧,其中每一行 C 代码前都添加了一个语句标号

1
2
3
4
5
6
0: mov  r1, 0         |  pc0: r1 = 0;
1: mov r2, 0 | pc1: r2 = 0;
2: addi r2, r2, 1 | pc2: r2 = r2 + 1;
3: add r1, r1, r2 | pc3: r1 = r1 + r2;
4: blt r2, 100, 2 | pc4: if (r2 < 100) goto pc2;
5: jmp 5 | pc5: goto pc5;

计算机执行以上的指令序列,最后会在 PC = 5 的指令处陷入死循环,此时计算已经结束,1+2+...+100的结果会被存放在寄存器r1

image-20220410183206046

  • 事实上,开拓者图灵早在 1936 年就已经提出了类似的核心思想,「计算机科学之父」果然名不虚传
  • 而这个流传至今的核心思想,就是“存储程序” !
  • 为了表达对图灵的敬仰,我们将上面这个最简单的计算机称为「图灵机,Turing Machine,TRM」
  • 或许你已经听说过图灵机这个作为计算模型时的概念
  • 但这里我们只强调一个最简单的真实计算机需要满足哪些条件:
    1. 结构上:TRM 有(存储器、PC、加法器、寄存器)
    2. 工作方式上:TRM 不断重复以下过程
    • 从 PC 指示的存储器位置取出指令 => 执行指令 => 更新 PC

  • 咦 ?(存储器、PC、加法器、寄存器)这些不都是数字逻辑电路课上学习过的部件吗 ?
  • 也许你会觉得难以置信,但你正面对着的这台无所不能的计算机,就是由只懂 0 和 1 的数字逻辑电路组成的 !

blame my 数字逻辑电路 teacher, blame myself.

  • 可我们写的是 C 代码啊,这个冷冰冰的电路是如何理解凝结了人类智慧结晶的 C 代码的呢 ?
  • 先驱说:“计算机诞生的那些年还没有 C 语言,大家当时都是直接编写对人类来说晦涩难懂的机器指令,这也是最早的对电子计算机的编程方式了”
  • 后来人们发明了(高级语言 & 编译器),我们能直接编写高级语言代码,经由编译器处理,生成功能等价的,且 CPU 能理解的指令
  • CPU 执行这些指令,就相当于执行了我们写的代码

如今的计算机本质上仍然是”存储程序”这种工作方式

经过了无数计算机科学家们的努力,才使得我们今天可以轻松地使用计算机

(完)