gitbook/许式伟的架构课/docs/91007.md
2022-09-03 22:05:03 +08:00

181 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 02 | 大厦基石:无生有,有生万物
你好,我是七牛云许式伟。
在上一讲中,我们把“构建一个应用程序”类比成“构建一座大厦”,并从宏观全局的视角剖析了应用程序这座大厦的构成。今天,我们将更加近距离地去解剖这座大厦的地基:冯·诺依曼体系结构。
## 解剖架构的关键点是什么?
在解剖之前,我想和你先谈谈“解剖学”:**我们应该如何去分析架构设计中涉及的每一个零部件。换一句话说,当我们设计或分析一个零部件时,我们会关心哪些问题。**
**第一个问题,是需求。**这个零部件的作用是什么?它能被用来做哪些事情?(某种意义上来说更重要的是)它不会被用来做哪些事情?
你可能会说,呀,这个问题很简单,既然我设计了这个零部件,自然知道它是用来干嘛的。但实质上这里真正艰难的是“为什么”:为何这个零件被设计成用来干这些事情的,而不是多干一点事情,或者为什么不是少干某些事情?
**第二个问题,是规格。**这个零部件接口是什么样的?它如何与其他零件连接在一起的?
规格是零部件的连接需求的抽象。符合规格的零部件可以有非常多种可能的实现方案,但是,一旦规格中某个条件不能满足了,它就无法正常完成与其他零件的连接,以达到预期的需求目标。
规格的约束条件会非常多样化,可能是外观(比如形状和颜色),可能是交互方式(比如用键盘、鼠标,或者语音和触摸屏),也可能是质量(比如硬度、耐热性等等)。
那么,冯·诺依曼体系结构的需求和规格又是什么样的呢?
## 为“解决一切的问题”而生
冯·诺依曼体系结构不但是应用程序这座大厦的地基,同时也是整个信息科技的地基。
**当我们去审视整个信息科技时,仅把它形容为一座大厦显得如此不贴切,甚至你也不能用“一个城市”去形容它,事实上,它更像是一个无中生有的全新世界:在其中,有个体、有族群、有生态,还有喜怒哀乐。**
冯·诺依曼体系结构的迷人之处在于,从需求来说,它想解决一切问题。解决一切可以用“计算”来解决的问题。
“计算”的边界在哪里?今天我们还没有人能够真正说得清。计算能不能解决“智能”的问题?通过计算能力,计算机是否终有一天可以获得和人类一样的智能?
今天人工智能热潮的兴起,证明对于这个问题我们很乐观:计算终将解决智能的问题。尽管我们不能确定什么时候能够达到,但是让人欣慰的是,我们一直在进步 —— 如果人类智能无法完成进一步的进化,那么我们就一直一直在前进,最终无限逼近甚至超越人类智能。
甚至有科幻小说家设想例如在Google的“AlphaGo”大热后霍炬和西乔创作的漫画“BetaCat”计算机演进出超过人类的智能是生物进化的一个自然演进路径它将取代人类成为新的食物链顶端并最终基于其悠久的生命力去完成人类有限生命无法实现的星际航行之路。
## 冯·诺依曼体系的规格
为了实现“解决一切可以用‘计算’来解决的问题”这个目标,冯·诺依曼引入了三类基础零部件:
* 中央处理器;
* 存储;
* 输入输出设备。
首先我们来看看存储。它负责存放计算涉及的相关数据,作为计算的输入参数和输出结果。
我们日常见到的存储设备非常的多样化。比如中央处理器自己内置的寄存器、内存、传统机械硬盘、USB固态硬盘、光盘等等。
从中央处理器的角度存储可简单分为两类一类是内置支持的存储通过常规的处理器指令可直接访问比如寄存器、内存、计算机主板的ROM。一类是外置存储它们属于输入输出设备。中央处理器本身并不能直接读写其中的数据。
冯·诺依曼体系中涉及的“存储”,指的是中央处理器内置支持的存储。
我们再来看看输入输出设备。它是计算机开放性的体现,大大拓展了计算机的能力。每个设备通过一个端口与中央处理器连接。通过这个端口地址,中央处理器可以和设备进行数据交换。数据交换涉及的数据格式由设备定义,中央处理器并不理解。
但这并不影响设备的接入。设备数据交换的发起方(设备使用方)通常理解并可以解释所接收的数据含义。为了方便使用,设备厂商或操作系统厂商通常会提供设备相关的驱动程序,把设备数据交换的细节隐藏起来,设备的使用方只需要调用相关的接口函数就可以操作设备。
最后我们来看看中央处理器。它负责程序(指令序列)的执行。指令序列在哪里?也存放在存储里面。计算机加电启动后,中央处理器从一个固定的存储地址开始执行。
中央处理器支持的指令大体如下(我们在第一篇文章中也曾提到过):
* 计算类也就是支持我们大家都熟知的各类数学运算如加减乘除、sin/cos等等
* I/O类从存储读写数据从输入输出设备读数据、写数据
* 指令跳转类,在满足特定条件下跳转到新的当前程序执行位置、调用自定义的函数。
和“解决一切可以用‘计算’来解决的问题”这个伟大的目标相比,冯·诺依曼体系的三类零部件的规格设计显得如此精简。
为什么这么简洁的规格设计,居然可以解决这么复杂的需求?
## 需求是怎么被满足的?
我们来设想一下:假如今天让我们从零开始设计一个叫电脑的东西,我们的目标是“解决一切可以用‘计算’来解决的问题”。
对于这么含糊的需求,如果你是“电脑”这个产品的主架构师,你会如何应对?
让我们来分析一下。
一方面,需求的变化点在于,要解决的问题是五花八门包罗万象的。如何以某种稳定但可扩展的架构来支持这样的变化?而另一方面,需求的稳定之处在于,电脑的核心能力是固定的,怎么表达电脑的核心能力?
电脑的核心能力是“计算”。什么是计算?计算就是对一个数据(输入)进行变换,变为另一个数据(输出)。在数学中我们把它叫“函数”。如下:
> y = F(x)
这里 x、y 是数据。它们可能只是一个简单的数值,也可能是文本、图片、视频,各种我们对现实问题进行参数化建模后的测量值,当然也可能是多个输入数据。但无论它的逻辑含义为何,物理上都可以以一段连续的字节内容来表达。用 Go 的语法表达就是:
```
func F(x []byte) (y []byte)
```
那么 x、y 物理上在哪里?思路推理到这里,“存储” 这个概念自然就产生了:存储,就是存放计算所要操作的数据的所在。
下面的问题是:一个具体的计算(也就是 F 函数)怎么表达?
这里的难点在于F 对于电脑的架构师来说是未知的。那么,怎么设计一种系统架构让用户可以表达任意复杂的计算(函数)?
逻辑上来看,无论多复杂的自定义函数,都可以通过下面这些元素的组合来定义:
* 内置函数比如整数或小数运算加减乘除、sin/cos等
* 循环和条件分支;
* 子函数(也是自定义函数)。
这样一来,对于任意的一个具体的计算(自定义函数)来说,都可以用一组指令序列来表达。
那么函数 F 物理上在哪里?以指令序列形式存放在存储里面。所以,存储不只存放计算所要操作的数据,也存放“计算”本身。
只是存储里面存放的“计算”只是数据需要有人理解并执行这些数据背后的计算行为才变成真正意义的“计算”。这个执行者就是中央处理器CPU。它支持很多计算指令包括执行内置函数、循环和条件分支、执行子函数等。
所以,有了中央处理器+存储,就可以支持任意复杂的“计算”了。
![](https://static001.geekbang.org/resource/image/cf/37/cf77b8fbe8a559cecbb264c390bc7337.png)
只是如果电脑只有“中央处理器+存储”,那它就如同一个人只有头脑而没有四肢五官,尽管很可能很聪明,但是这种聪明无法展现出来,因为它没法和现实世界发生交互。
交互,抽象来看就是输入和输出。对人来说,输入靠的是五官:眼睛看、耳朵听、鼻子闻、舌头尝,以及肌肤接触产生的触觉。输出靠语言(说话)和各种动作,如微笑、眨眼、皱眉、手势等等。
对于电脑来说,输入输出的需求就更多了,不只是四肢五官,而可能是千肢万官。
从输入需求来说可能采集静态图像、声音、视频也可能采集结构化数据如GPS位置、脉搏、心电图、温度、湿度等还可能是用户控制指令如键盘按键、鼠标、触摸屏动作等。
从输出需求来说,可能是向屏幕输出信息;也可能是播放声音;还可能是执行某项动作,如交通灯开关、汽车马达转动、打印机打印等。
但不管是什么样交互用途的器官(设备),我们要做的只是定义好统一的数据交换协议。这个数据交换机制,和网络上两台电脑通过互联网,需要通过某种数据交换协议进行通讯,需求上没有实质性的差别。
也就是说除了纯正的“计算”能力外中央处理器还要有“数据交换”能力或者叫IO能力。最终**电脑可以被看做由 “中央处理器+存储+一系列的输入输出设备”** 构成。如下图:
![](https://static001.geekbang.org/resource/image/28/a9/28ef9c0241c5c34abb85148453379fa9.png)
尽管输入输出设备引入的最初灵感可能是来自于“交互”,但是当我们去审视输入输出设备到底是什么的时候,我们很自然发现,它能够做的不单单是交互。
比如常见的外置存储如机械硬盘、光盘等,它们也是输入输出设备,但并不是用于交互,而是显著提升了电脑处理的数据体量。
输入输出设备从根本上解决的问题是什么?
是电脑无限可能的扩展能力。
最重要的一点,输入输出设备和电脑是完全异构的。输入输出设备对电脑来说就只是实现了某项能力的黑盒子。
这个黑盒子内部如何没有规定。它可以只是一个原始的数字化的元器件也可以是另一台冯·诺依曼架构的电脑还可以是完全不同架构的电脑比如GPU电脑、量子计算机。
你可以发现,引入了输入输出设备的电脑,不再只能做狭义上的“计算”(也就是数学意义上的计算),如果我们把交互能力也看做一种计算能力的话,电脑理论上能够解决的“计算”问题变得无所不包。
## 架构思维上我们学习到什么?
架构的第一步是需求分析。从需求分析角度来说,关键要抓住需求的稳定点和变化点。需求的稳定点,往往是系统的核心价值点;而需求的变化点,则往往需要相应去做开放性设计。
对于“电脑”这个产品而言,需求的稳定点是电脑的“计算”能力。需求的变化点,一是用户“计算”需求的多样性,二是用户交互方式的多样性。
电脑的“计算”能力,最终体现为中央处理器的指令集,这是需求相对稳定的部分。
用户“计算”需求的多样性,最终是通过在存储中的指令序列实现。计算机加电启动后,中央处理器并不是按自己固有的“计算”过程进行,而是从一个固定的存储地址加载指令序列执行。
通常这个固定的存储地址指向计算机主板的ROM上的一段启动程序BIOS。这段启动程序通常包含以下这些内容。
* 存储设备的驱动程序用以识别常规的外置存储设备比如硬盘、光驱、U盘。
* 基础外部设备的驱动程序,比如键盘、鼠标、显示器(显卡)。
* 设备和启动配置的基础管理能力。
* 在外置存储上执行程序的能力(中央处理器只支持在内存上执行程序,当然它也为在外置存储执行程序提供了一些支持,比如内存页缺失的中断处理)。
* 将执行权转移到外置存储(第一次安装操作系统的时候可能是光驱甚至是网络存储,平常通常是硬盘)上的操作系统启动程序。这样,操作系统就开始干活了。
这样一来“计算”需求的多样性只需要通过调整计算机主板上的BIOS程序乃至外置存储中的操作系统启动程序就可以实现而不必去修改中央处理器本身。
用户交互方式的多样性,则通过定义外部设备与中央处理器的数据交换协议实现。
当我们把所有的变化点从电脑的最核心部件中央处理器剥离后,中央处理器的需求变得极其稳定,可独立作为产品进行其核心价值的演进。
## 结语
总结一下,今天,我们近距离地去解剖了整个信息世界地基:冯·诺依曼体系结构。
冯·诺依曼体系结构的不凡之处在于,它想“解决一切可以用‘计算’来解决的问题”。
为了实现这个目标,冯·诺依曼引入了三类基础零部件:中央处理器、存储、输入输出设备。所有计算机都可以看做由 “中央处理器+存储+一系列的输入输出设备” 构成。
为了方便理解,我在尝试用 Go 语言模拟来实现冯·诺依曼架构体系的电脑:
* [https://github.com/qiniu/arch/tree/master/von](https://github.com/qiniu/arch/tree/master/von)
如果你对此感兴趣,欢迎 fork 并对其进行修改迭代。
如果你对今天的内容有什么思考与解读,欢迎给我留言,我们一起讨论。如果你觉得有所收获,也欢迎把文章分享给你的朋友。感谢你的收听,我们下期再见。