在计算机发展的早期,硬件昂贵而程序员廉价。事实上,程序员非常廉价,他们甚至不被称为“程序员”,而实际上通常是数学家或电气工程师。早期的计算机被用来快速解决复杂的数学问题,因此数学家自然非常适合“编程”这项工作。
什么是程序?
首先,了解一点背景知识。计算机本身无法做任何事情,因此它们需要程序来驱动其行为。程序可以被认为是非常详细的配方,它接受输入并产生输出。配方中的步骤由操作数据的指令组成。虽然这听起来很复杂,但您可能知道这个语句是如何工作的
1 + 2 = 3
加号是“指令”,而数字 1 和 2 是数据。在数学上,等号表示方程的两边是“等价的”,但是大多数计算机语言使用某种等号的变体来表示“赋值”。如果计算机正在执行该语句,它会将加法的结果(“3”)存储在内存中的某个位置。
计算机知道如何用数字进行数学运算,并在机器的内存层次结构中移动数据。我不会过多地谈论内存,除了它通常有两种不同的类型:快速/小和慢速/大。CPU 寄存器非常快、非常小,并且充当暂存器。主内存通常非常大,但速度远不及寄存器内存。CPU 在程序执行时,会将它们正在处理的数据从主内存洗牌到寄存器,然后再洗牌回来。
汇编器
计算机非常昂贵,而人很廉价。程序员花费了无数的时间将手写的数学公式翻译成计算机可以执行的计算机指令。最早的计算机具有糟糕的用户界面,有些只包含前面板上的拨动开关。这些开关代表内存中单个“字”的 1 和 0。程序员会配置一个字,指示将其存储在何处,并将该字提交到内存。这既耗时又容易出错。

程序员 Betty Jean Jennings (左)和 Fran Bilas (右)在摩尔电气工程学院操作 ENIAC 的主控制面板。(美国陆军照片,来自 ARL 技术图书馆的档案)
最终,一位 电气工程师 认为他的时间并不廉价,并编写了一个程序,其输入是以人们可以阅读的方式表达的“配方”,该程序输出计算机可读的版本。这是第一个“汇编器”,它引起了很大的争议。拥有昂贵机器的人们不想在人们已经在做的事情上“浪费”计算时间;尽管速度慢且容易出错。随着时间的推移,人们开始欣赏汇编器相对于手工汇编程序的的速度和准确性,以及计算机完成的“实际工作”量增加。
虽然汇编器程序比将位模式拨动到机器的前面板有了很大的进步,但它们仍然非常专业化。上面的加法示例可能看起来像这样
01 MOV R0, 1
02 MOV R1, 2
03 ADD R0, R1, R2
04 MOV 64, R0
05 STO R2, R0
每一行都是一条计算机指令,以指令的简写名称开头,后跟指令操作的数据。这个小程序将首先将值 1 “移动”到名为 R0 的寄存器中,然后将 2 移动到寄存器 R1 中。第 03 行将寄存器 R0 和 R1 的内容相加,并将结果值存储到寄存器 R2 中。最后,第 04 行和第 05 行标识结果应存储在主内存中的位置(地址 64)。管理数据在内存中的存储位置是编写计算机程序中最耗时且容易出错的部分之一。
编译器
汇编比手工编写计算机指令要好得多;然而,早期的程序员渴望像他们习惯于编写数学公式那样编写程序。这推动了更高级别编译语言的开发,其中一些语言是历史脚注,而另一些语言至今仍在使用。 ALGO 就是这样一个脚注,而今天,Fortran 和 C 等语言仍在解决实际问题。 Fortran 和 C。

编程语言的族谱树,Algol 和 Fortran 家族
这些“高级”语言的引入使程序员能够以更简单的术语编写程序。在 C 语言中,我们的加法汇编程序将写成
int x;
x = 1 + 2;
第一条语句描述了程序将使用的一块内存。在这种情况下,内存应为整数大小,其名称为 x。第二条语句是加法,尽管是“向后”写的。C 程序员会将其理解为“X 被赋值为 1 加 2 的结果”。请注意,程序员不需要说明将 x 放在内存中的什么位置,因为编译器会处理这个问题。
一种新型程序,称为“编译器”,它会将用高级语言编写的程序转换为汇编语言版本,然后通过汇编器运行它,以生成程序的机器可读版本。程序的这种组合通常称为“工具链”,因为一个程序的输出直接发送到另一个程序的输入。
编译语言相对于汇编语言程序的巨大优势在于可以从一种计算机型号或品牌移植到另一种计算机型号或品牌。在计算机发展的早期,来自 IBM、数字设备公司、德州仪器、UNIVAC、惠普等公司的不同类型的计算硬件呈爆炸式增长。除了需要插入电源之外,这些计算机几乎没有共同之处。内存和 CPU 架构差异很大,将程序从一台计算机翻译到另一台计算机通常需要数人年。
使用高级语言,编译器工具链只需移植到新平台即可。一旦编译器可用,就可以针对新计算机重新编译高级语言程序,而几乎不需要修改。高级语言的编译确实是革命性的。

IBM PC XT 于 1983 年发布
程序员的生活变得非常美好。使用高级语言表达他们想要解决的问题变得容易得多。由于半导体技术的进步和集成芯片的发明,计算机硬件的成本急剧下降。计算机变得更快、更强大,而且价格也便宜得多。在某个时候,可能是在 80 年代后期,出现了反转,程序员变得比他们使用的硬件更昂贵。
解释器
随着时间的推移,一种新的编程模型兴起,其中一个名为“解释器”的特殊程序会读取程序并将其转换为立即执行的计算机指令。解释器将程序作为输入,并像编译器一样将其解释为中间形式。与编译器不同,解释器随后执行程序的中间形式。这在每次运行解释型程序时都会发生,而编译型程序只编译一次,计算机“按编写方式”执行机器指令。
顺便说一句,当人们说“解释型程序很慢”时,这是感知到的性能不足的主要来源。现代计算机功能如此强大,以至于大多数人无法分辨编译型程序和解释型程序之间的区别。
解释型程序,有时称为“脚本”,甚至更容易移植到不同的硬件平台。由于脚本不包含任何特定于机器的指令,因此程序的单个版本可以在许多不同的计算机上运行而无需更改。当然,关键是必须将解释器移植到新机器才能实现这一点。
perl 就是一种非常流行的解释型语言的例子。我们加法问题的完整 perl 表达式将是 perl。
$x = 1 + 2
虽然它看起来和行为很像 C 版本,但它缺少变量初始化语句。还有其他差异(这超出了本文的范围),但您可以看到,我们可以编写一个计算机程序,它非常接近数学家用铅笔和纸手工编写程序的方式。
虚拟机
最新的编程模型热潮是虚拟机,通常缩写为 VM。虚拟机有两种类型:系统虚拟机和进程虚拟机。两种类型的 VM 都提供了与“真实”计算硬件的抽象级别,尽管它们的范围不同。系统虚拟机是提供物理硬件替代品的软件,而进程虚拟机旨在以系统独立的方式执行程序。因此,在这种情况下,进程虚拟机(以下简称虚拟机)在范围上与解释器相似,因为程序首先被编译成中间形式,然后虚拟机执行它。
解释器和虚拟机之间的主要区别在于虚拟机实现了通过其虚拟指令集访问的理想化 CPU。这种抽象使得编写前端语言工具成为可能,这些工具可以编译用不同语言编写的程序并以虚拟机为目标。可能最流行和最广为人知的虚拟机是 Java 虚拟机 (JVM)。JVM 最初仅用于 1990 年代的 Java 编程语言,但现在它托管了 许多 流行的计算机语言:Scala、Jython、JRuby、Clojure 和 Kotlin,仅举几例。还有其他可能不为人知的例子。我最近才知道我最喜欢的语言 Python 不是一种解释型语言,而是一种 托管在虚拟机上的语言!
虚拟机延续了历史趋势,即减少程序员在支持其特定领域需求的语言中表达问题所需的平台特定知识量。
结束了
我希望您喜欢这篇关于软件中一些不太可见部分的入门文章。您希望我接下来深入探讨其他主题吗?请在评论中告诉我。
本文最初发表于 PyBites,经许可转载。
4 条评论