You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

108 lines
8.7 KiB
Markdown

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

# 03 | 汇编:编程语言的诞生
你好,我是七牛云许式伟。
在上一讲中,我们一起解剖了架构大厦的地基:冯·诺依曼体系。接下来,我们就开始沿着这座大厦攀登,一起来聊聊编程语言。
对于现代计算机来说,虽然 CPU 指令是一个很有限的指令集但是CPU 执行的指令序列(或者叫“程序”)并不是固定的,它依赖于保存在存储中的数据,由软件工程师(或者叫“程序员”)编写的软件决定。
从上一讲中我们可以知道计算机的程序可能被保存在计算机主板的ROM上这段程序也叫计算机的启动程序也可能被保存在外置的存储设备比如硬盘并在合适的时机加载执行。
程序称得上是计算机的灵魂。指令序列的可能性是无穷的,程序的可能性就是无穷的。今天计算机创造的世界如此多姿多彩,正是得益于程序无穷的可能性。
那么,软件工程师是怎么编写程序的?
## 编程的史前时代
在第一门面向程序员的编程语言出现前人们只能通过理解CPU指令的二进制表示将程序以二进制数据方式刻录到存储比如ROM或硬盘上。
这个时期的编程无疑是痛苦的,效率是极其低下的:且不说我们怎么去修改和迭代我们的程序,光将我们的想法表达出来就极其困难。
我们首先要把表达的执行指令翻译成二进制的比特数据,然后再把这些数据刻录到存储上。
这个时候软件和硬件的边界还非常模糊,并不存在所谓软件工程师(或者叫“程序员”)这样的职业。写程序也并不是一个纯软件的行为,把程序刻录到存储上往往还涉及了硬件的电气操作。
为了解决编程效率的问题汇编语言和解释它的编译器诞生了。汇编语言的编译器将汇编语言写的程序编译成为CPU指令序列并将其保存到外置的存储设备比如硬盘上。
汇编语言非常接近计算机的CPU 指令一条汇编指令基本上和CPU指令一一对应。
## 与机器对话
汇编语言的出现,让写程序(编程)成为一个纯软件行为(出现“程序员”这个分工的标志),人们可以反复修改程序,然后通过汇编编译器将其翻译成机器语言,并写入到外置的存储设备(比如硬盘)。并且,程序员可以按需执行该程序。
在表达能力上,汇编语言主要做了如下效率优化。
* 用文本符号symbol表达机器指令例如 add 表示加法运算,而不用记忆对应的 CPU 指令的二进制表示。
* 用文本符号symbol表达要操作的内存地址并支持内存地址的自动分配。比如我们在程序中使用了“Hello” 这样一段文本,那么汇编编译器将为程序开辟一段静态存储区(通常我们叫“数据段”)来存放这段文本,并用一个文本符号(也就是“变量名-variable”指向它。用变量名去表达一段内存数据这样我们就不用去关注内存的物理地址而把精力放在程序的逻辑表达上。
* 用文本符号symbol表达要调用的函数function也叫“过程-procedure”地址。对 CPU 指令来说,函数只有地址没有名字。但从编程的角度,函数是机器指令的扩展,和机器指令需要用文本符号来助记一样,函数的名称也需要用文本符号来助记。
* 用文本符号symbol表达要跳转的目标地址。高级语言里面流程控制的语法有很多比如 goto、if .. else、for、while、until 等等。但是从汇编角度来说只有两种基本的跳转指令无条件跳转jmp和条件跳转(je、jne)。同样,跳转的目标地址用文本符号(也就是“标签-label”有助于程序逻辑的表达而不是让人把精力放在具体的指令跳转地址上。
总结来说,汇编从指令能力上来说,和机器指令并无二致,它只不过把人们从物理硬件地址中解脱出来,以便专注于程序逻辑的表达。
但是这一步所解放的生产力是惊人的毕竟如果有选择的话没有人会愿意用0101这样的东西来表达自己的思想。
## 可自我迭代的计算机
从探究历史的角度,你可能会期望了解最真实的历史发展过程。比如:怎么产生了现代计算机(以键盘作为输入,显示器作为输出)?怎么产生了汇编语言?怎么产生了操作系统?
不过本专栏是以架构设计为目的我们目的并不是还原最真实的历史。架构的意义在于创造。我们甚至可以设想一个有趣的场景假设今天我们的信息科技的一切尚不存在那么从架构设计角度我们从工程上来说如何更高效地完成从0到1的信息科技的构建
> 最早的输入输出设备并不是键盘和显示器而是打孔卡和打印机。用打孔卡来作为机器指令的输入早在18世纪初就被用在织布机上了。早期的数字计算机就是用打孔卡来表达程序指令和输入的数据。
>
> 下图是 IBM 制造的打孔卡:
![](https://static001.geekbang.org/resource/image/90/99/907ef3466d15146c8aa1b2ea2a7dbd99.png)
我们可以想象一下,第一台以键盘+显示器为标准输入输出的现代计算机出现后一个最小功能集的计算机主板的ROM上应该刻上什么样的启动程序换句话说这个现代计算机具备的最基本功能是什么
从高效的角度(不代表真实的历史,真实历史可能经历过很多曲折的发展过程),我想,它最好具备下面的这些能力。
* 键盘和显示器的驱动程序。
* 当时最主流的外置存储设备(不一定是现代的硬盘)的驱动程序。
* 一个汇编程序编辑器。可从存储中读取汇编程序代码,修改并保存到存储中。
* 一个汇编编译器。可将汇编程序代码编译成机器代码程序,并保存到存储中。
* 可以执行一段保存在外置存储设备中的机器代码程序。
本质上,我们是要实现一个最小化的计算能力可自我迭代的计算机。
这个时期还没有操作系统当然把ROM上的启动程序BIOS看做一种最小化的操作系统我觉得也可以但毕竟不是现实中我们说的操作系统
汇编语言的出现要早于操作系统。操作系统的核心目标是软件治理,只有在计算机需要管理很多的任务时,才需要有操作系统。
所以在没有操作系统之前BIOS 包含的内容很可能是下面这样的:
* 外置存储设备的驱动程序;
* 基础外部设备的驱动程序,比如键盘、显示器;
* 汇编语言的编辑器、编译器;
* 把程序的源代码写入磁盘,从磁盘读入的能力。
最早期的计算机毫无疑问是单任务的,计算的职能也多于存储的职能。每次做完任务,计算机的状态重新归零(回到初始状态)都没有关系。
但是,有了上面这样一个 BIOS 程序后,计算机就开始发展起它存储的能力:程序的源代码可以进行迭代演进了。
这一步非常非常重要。计算机的存储能力的重要性如同人类发明了纸。纸让人类存储了知识,一代代传递下去并不断演进,不断发扬光大。
而同样有了存储能力的计算机,我们的软件程序就会不断被传承,不断演进发扬光大,并最终演进出今天越来越多姿多彩的信息科技的世界。
## 结语
今天我们一起回到了编程的史前时代,共同回溯了编程语言诞生的历史。
为了不再用“0101”表达自己的思想人们创造了汇编语言这一步让编程成为一个纯软件行为程序员这一个分工也由此诞生。
为了进一步支持程序员这个职业我们设计了MVP版最小化可行产品的可自我迭代的计算机。有了这个计算机我们就可以不断演进并最终演进出今天越来越多姿多彩的信息科技的世界。
## 架构上的思考题
在上一讲中,我们谈架构思维时提到,我们在需求分析时,要区分需求的变化点和稳定点。稳定点往往是系统的核心能力,而变化点则需要对应地去考虑扩展性上的设计。
今天,我们假设要实现一个最小化的计算能力可自我迭代的计算机,需求如上所述。
那么,它的变化点和稳定点分别是什么?为此,你会怎么设计出哪些子系统,每个子系统的规格是什么?扩展性上有哪些考虑?
欢迎把你的想法告诉我,我们一起讨论。感谢你的收听,再见。