软件,从基本原理出发
摘要
一篇从基本原理出发解释计算和软件基础的文章,旨在为普通读者揭开计算机工作原理的神秘面纱。
暂无内容
查看缓存全文
缓存时间: 2026/07/03 23:17
# 软件,从第一性原理出发 · Faza
来源:https://fazamhd.com/mental-models/software/
当你使用智能手机或笔记本电脑时,很容易忘记其背后物理现实的惊人之处。在那无缝体验的背后,凝聚了物理学家、化学家、数学家、工程师、程序员和设计师数百年的努力。你的设备内部,是一块近乎完美的纯净岩石切片,上面雕刻着微观电路。仅仅通过控制这些电路中电子的流动,我们就能让这块岩石为我们计算、记忆和思考,从而省去我们的脑力劳动。单次操作耗时不到一纳秒,让你在眨眼之间就能执行数十亿次计算。这一切运行得如此无缝,以至于作为用户,我们完全无需面对这些复杂性,可以自由地专注于手头的任务。
**英特尔奔腾II Dixon 处理器芯片显微照片**(1998年)。每个矩形区域都是一个独立的功能模块,负责存储数据、执行计算或激活特定电路,蚀刻在大约两平方厘米的硅片上。你甚至可以凭肉眼区分内存和逻辑电路:左侧巨大的规则网格是缓存(数百万个相同的存储单元),而右侧不规则的区块是计算电路。(来源:维基共享资源)
然而,虽然我们可以忽略底层的一切是如何运作的,但随着软件在我们生活中占据主导地位,这样做正变得越来越困难。事实上,数字世界现在对我们日常决策的影响力已不亚于物理世界。正如我们学习物理或生物基础来理解物理环境一样,理解软件对于驾驭我们的数字环境已变得至关重要。缺乏这种基本素养,我们就有可能被那些旨在捕获我们时间和注意力的系统被动管理或利用,这些系统正是利用了我们因不了解其工作原理而产生的焦虑。有了这种理解和清晰的认识,我们就能获得更好地使用技术、对我们的数字安全做出明智决策、甚至构建满足自身特定需求的软件的自主能力。
本文旨在剥去软件和计算机的“魔法”外衣,表明理解它们的工作原理并非计算机科学学位才能享有的特权。为此,我将尽可能避免使用行话,只引入必要的概念,并运用第一性原理思维逐步构建。在引擎盖之下,计算并非一份枯燥的技术规格清单,而是一个关于人类智慧与实际问题解决的迷人故事,每一层新的构建都基于前一层。跟随这个故事,你可以建立清晰的思维模型,更清楚地了解使用设备时幕后发生的事情。你不仅会理解技术如何运作,还会了解内置的、保护你的无形安全措施。我已尽量保持本文简洁,只保留了展示这些思想如何形成所必需的历史背景。
## 操控物理来完成数学计算
世界上大部分解决问题的方式都涉及数学计算,无论是预测天气、计算飞机航线,还是检查桥梁的结构完整性。最初,“计算机”只是坐满了数学家、手工进行这些计算的房间,有些计算需要耗时数天。在战争时期和太空竞赛期间,赌注更高了:计算弹道导弹和太空火箭的轨迹需要解决成千上万个复杂方程。由于即使是微小的错误也可能导致火箭爆炸、导弹偏离目标或桥梁倒塌,这些计算必须一遍又一遍地验证。这个过程非常缓慢、昂贵且容易出错。
**NACA高速飞行站计算机室,1949年。** 爱德华兹空军基地的NACA高速飞行站计算机室(1949年)。在成为机器之前,“计算机”是一个工作头衔,这些数学家用手工处理试飞数据。(来源:NASA)
人们迫切需要一种能够可靠且快速执行计算的机器。科学家们寻找能够模拟计数的物理过程,早期的尝试之一是机械计算器,由互锁的齿轮和杠杆构建而成。但它也有自身的问题:机械部件会随时间磨损、需要持续维护,而且本质上速度很慢。
**巴黎工艺美术博物馆的帕斯卡计算器(Pascaline)。** 布莱兹·帕斯卡发明的机械计算器Pascaline(1642年),藏于巴黎工艺美术博物馆。每个刻度盘设置一个十进制位,结果出现在上方的窗口行中,其编号轮和进位机制与下面的模拟器相同。(来源:维基共享资源)
下面的模拟器展示了机械计算器的基本构建块:一排编号轮,每个十进制位(百位、十位、个位)一个,就像汽车里的里程表。加一会使个位轮向前旋转一个数字。当它从`9`滚到`0`时,一个**进位销**会啮合并将十位轮向前推一个数字。如果十位轮本身在`9`,它自己的进位销也会啮合,在同一动作中拖动百位轮,所有通过进位销啮合的轮子一起转动。
请注意,轮子不像普通齿轮那样持续接触(那样会让它们以相同速度旋转);进位销只在每转十圈的那个短暂瞬间伸进去啮合下一个轮子。我们可以通过添加更多轮子来支持更大的数字,也可以想象通过改变旋转方向或添加更多机械部件来进行其他计算。
**机械计数器**
数值:000
个位0123456789 十位0123456789 百位0123456789
每个齿轮每点击一次向前转动一个齿。当它从9转回0时,其进位齿会触发下方的销轮,将下一个齿轮向前推动一个齿。
真正的飞跃发生在我们从机械部件转向电力,使用电开关作为基本构建块。我们谈论的不是家里的物理电灯开关,而是通过施加电压自动控制的开关。电开关有两种状态:断开(无电流流动,代表`0`或`假`)和闭合(电流流动,代表`1`或`真`)。
通过以不同方式将这些开关连接起来,我们可以做出逻辑决策。这些配置中最基本的被称为**逻辑门**:
- **与门(串联)**:如果我们将两个开关一前一后放置,只有当开关A **与** 开关B 都闭合时,电流才会流动。
- **或门(并联)**:如果我们将两个开关并排放置,当开关A **或** 开关B 闭合时,电流就会流动。
- **非门**:一个被设计为“常闭”(默认允许电流通过)的开关,只有当对其施加电压时,它才会断开并切断电流。
-
A B │ 灯泡
0 0 │ 0
0 1 │ 0
1 0 │ 0
1 1 │ 1
点击端子 A 或 B 切换
开关 A = 0,开关 B = 0 ⇒ 灯泡 = 0
串联电路:只有当开关 A **和** 开关 B 都闭合(1)时,电流才能流向灯泡。
我们可以使用这三种门的组合来构建*任何*逻辑规则,无论多么复杂。将任何逻辑规则视为一个表格,将输入组合映射到输出(`开`或`关`)。要使用硬件重建此表格,我们只需关注输出为`开`的行。
为了构建对应任何`开`行的电路,我们使用一个`与门`来检测该确切的输入组合。如果在该行中某输入应该是`关`,我们首先让它通过一个`非门`。这将`关`信号翻转为`开`,满足`与门`(其所有输入均为`开`时才触发)。
最后,由于整体输出应在满足第一个目标组合 **或** 第二个组合时开启,我们将所有检测行的`与`输出馈送到一个单独的`或门`。
因为每个逻辑规则都可以表示为一个表格,而任何表格都可以使用这种精确的方法构建,所以 **与、或、非** 这三者组合在数学上是完备的,能够表示任何可以想象的逻辑规则。
为了直观地看到这一点,下面的模拟器构建了**异或**规则,只有当输入不同时,输出才为`开`。正如我们将在下一节中看到的,这正是执行二进制加法所需的逻辑。
**如何构建任何逻辑电路(异或示例)**
A=0,B=1 ➜ 输出=1
#### 目标真值表
我们希望最终输出仅在高亮行开启。我们为每一行构建一个与门检测器。
#### 逻辑电路构建
注意活动信号(以**青色**着色)如何流动。每个目标行都有自己的检测器,它们在底部汇合。
非门 非门 与门 与门 或门
0 1
A B
1
输出
!A =1 !B =0 ✔ 活动检测器
由于开关只有两种状态,我们无法用通常的十个数字(`0`到`9`)来表示数字。相反,我们使用**二进制**,它完全依赖于`0`和`1`(每个数字称为一个**比特**,是**二进制数字**的缩写)。
二进制计数的工作原理与机械齿轮计数器完全相同,但只有两个数字。在我们熟悉的十进制系统中,9+1会强制从9滚到0并产生一个进位1,形成10。在二进制中,因为`1`是最高数字,所以1+1会立即强制滚到0并产生进位1。它写作10₂(代表十进制数2)。
**二进制计数器**
0000 = 0
每次点击翻转比特0。当一个比特从1滚回0时,进位会传播到它左边的下一个比特。
要了解这些开关如何执行数学运算,让我们看看将两个一位数A和B相加时会发生什么:
- 0₂ + 0₂ = 00₂(总和:0₂,进位:0₂)
- 1₂ + 0₂ = 01₂(总和:1₂,进位:0₂)
- 0₂ + 1₂ = 01₂(总和:1₂,进位:0₂)
- 1₂ + 1₂ = 10₂(总和:0₂,进位:1₂)
注意其中的模式:
- **总和**位仅在恰好一个输入开启时(但非两者都开启)为`1`。这正是**异或**门的行为。
- **进位**位仅在两个输入都开启时为`1`。这正是**与**门的行为。
下面的模拟器将你刚构建的异或电路与一个与门连接起来,成为这个**半加器**:切换A和B,观察总和位和进位位如何一起更新。
**半加器**
A B │ 总和 进位
0 0 │ 0 0
0 1 │ 1 0
1 0 │ 1 0
1 1 │ 0 1
点击输入 A 或 B 切换
0+0=00₂ = 0
内部没有算术逻辑,异或门计算总和(1s列),与门计算进位(2s列)。
通过将这些门连接起来,并将进位输出馈送到下一个位的输入,我们可以添加任意大小的二进制数。这被称为**纹波进位加法器**,来自比特0的进位级联到比特1,比特1本身可能产生进位到比特2,依此类推,直到最终总和稳定。由于减法就是加一个负数,而乘法是重复加法,每个算术运算都可以简化为这些基本门电路的组合。
在早期计算机中,我们使用**真空管**来实现这些开关。它们类似于密封的玻璃灯泡,内部包含一个加热的灯丝和一个密封在真空中的金属板。加热灯丝使其电子获得足够能量沸腾到真空中,这个过程称为**热电子发射**,带正电的极板将它们吸引穿过间隙。第三个元件,一个线网**栅极**,位于两者之间。对其施加负电压会排斥电子,使其在到达对面之前被弹回,从而切断电流;释放该电压则让电流再次流动。这是一个快速、无活动部件的开关。
**真空管(三极管)**
阳极板 (+150V)
栅极 (-5V)
加热灯丝
负载
关 (0)
**工作原理:**灯丝被加热,释放电子。但由于控制栅极带**负电 (-5V)**,它会将电子向下排斥。没有电流能跨越真空到达阳极。开关**关 (0)**。
**Western Electric VT-1 三极管真空管**(1917年),一款经典的第一次世界大战时期三极管真空管。(来源:维基共享资源)
但随着工程师们试图构建更大、功能更强的机器,他们遇到了物理瓶颈。真空管笨重、能耗巨大,且经常故障。为了进一步创新,他们不能只是优化易碎的玻璃器皿;必须回到物理基本原理,发明一种更好的开关。这导致了1947年第一个**晶体管**的发明,这是一种固态电子开关,比真空管更小、更冷、更可靠。这个最初的器件证明了固体材料可以在没有真空和热灯丝的情况下切换电流。
今天几乎每颗芯片中使用的改进设计是后来出现的。电压被施加到位于硅片上方薄绝缘层上的**栅极**电极上。该电压产生一个电场,穿过绝缘体将硅自身的电子吸引到其表面,在两个端子——**源极**和**漏极**——之间形成一个薄薄的导电沟道;移除电压则会破坏沟道并切断电流,从而在没有活动部件的情况下切换电流的开或关。
**晶体管(MOSFET)**
源极 漏极
栅极
绝缘层
负载
关 (0)
**工作原理:**当栅极电压为**0V**时,源极和漏极之间的硅保持非导性,不形成沟道。无电流流动。晶体管**关 (0)**。
**第一个点接触晶体管**。1947年贝尔实验室开发的原始点接触晶体管。(来源:贝尔实验室)
在随后的几十年里,随着我们开发出将成千上万个这样的晶体管直接印刷到硅晶圆上的方法,我们创造了**集成电路**(或芯片)。这些印刷晶体管的尺寸和间距被称为**工艺节点**(或制造工艺),以纳米为单位测量。工艺节点越小,每个晶体管就越小,允许我们在相同的物理空间内塞入更多的晶体管。今天,我们手中的智能手机集成了巨大的计算能力和内存,而功耗仅为早期超级计算机的一小部分,那些超级计算机占据了整个房间,并产生了大量热量。
## 将现实转化为比特
我们已经看到,两态开关如何允许我们使用二进制系统来表示和计算数字。但我们不想把自己局限于数字和逻辑;我们的大多数现实世界并不能整齐地排列成数字。例如,声音是空气压力的连续波动。早期的电话线通过将说话者声音的物理振动转化为电信号电压的等效波动(使用麦克风振膜),并在收听者端进行反向转换(使用电磁体和扬声器)来传输声音。但是,我们如何使用逻辑门来表示这种连续的、波动的电波呢?
灵感来自于贝尔实验室的科学家们——通信技术的先驱——在电话线上遇到了瓶颈。他们无法同时发送多个信号,因为它们会相互干扰。他们还不得不在不同点使用放大器来补偿信号在远距离传输中的衰减。任何环境干扰都可能引入噪声,而放大器甚至会将噪声放大。他们意识到,将语音作为连续的模拟波传输并不可靠,一定有更好的方法。
克劳德·香农,贝尔实验室的一位数学家,形式化……
相似文章
从第一原理看函数式编程,第1部分——动机
本文从第一原理介绍函数式编程,涵盖函数的数学定义及编程语言范式的分类。这是面向命令式编程者系列文章的第一部分。
系统编程入门,第一部分:程序员编写程序(2025)
一篇系统编程入门文章,涵盖诸如位操作、解析、文件系统、系统调用和内存管理等基础知识,面向程序员。
网络与互联网,从基本原理出发
一篇深入的教育文章,从基本原理出发解释网络与互联网,涵盖分组交换、TCP、DNS、TLS以及数据如何在全球传输。
迈向可理解的软件
本文批判了当前的编程实践和对大语言模型的依赖,反而主张通过更好的抽象、文档和软件栈来使代码更易于理解和维护。
软件关乎人,而非代码(2020)
一篇论述软件成功更多取决于理解人及其需求,而非编写完美代码的文章,并以被遗弃的、未解决实际问题的代码库为例。