惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Cloudbric
Cloudbric
aimingoo的专栏
aimingoo的专栏
D
Docker
量子位
阮一峰的网络日志
阮一峰的网络日志
The GitHub Blog
The GitHub Blog
小众软件
小众软件
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - Franky
S
Schneier on Security
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
T
Tor Project blog
Google Online Security Blog
Google Online Security Blog
T
Tenable Blog
C
Cyber Attacks, Cyber Crime and Cyber Security
I
InfoQ
罗磊的独立博客
P
Palo Alto Networks Blog
Security Latest
Security Latest
K
Kaspersky official blog
Apple Machine Learning Research
Apple Machine Learning Research
I
Intezer
C
Cybersecurity and Infrastructure Security Agency CISA
TaoSecurity Blog
TaoSecurity Blog
S
Security @ Cisco Blogs
Google DeepMind News
Google DeepMind News
W
WeLiveSecurity
Simon Willison's Weblog
Simon Willison's Weblog
V
V2EX
N
Netflix TechBlog - Medium
爱范儿
爱范儿
S
Secure Thoughts
S
Securelist
MyScale Blog
MyScale Blog
Webroot Blog
Webroot Blog
IT之家
IT之家
Cyberwarzone
Cyberwarzone
Martin Fowler
Martin Fowler
Project Zero
Project Zero
NISL@THU
NISL@THU
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
H
Hacker News: Front Page
H
Heimdal Security Blog
博客园_首页
V
Vulnerabilities – Threatpost
H
Hackread – Cybersecurity News, Data Breaches, AI and More
Recent Announcements
Recent Announcements
Blog — PlanetScale
Blog — PlanetScale
The Cloudflare Blog

Oskyla 烹茶室

修复 Joplin on KDE 菜单栏显示问题 Copy Fail:Linux 内核 2017 年至今的高危漏洞(附临时缓解方案) | CVE-2026-31431 Hermes Agent — 在 K3s / K8s 中运行指南 在 K3s 节点上安装并使用 nerdctl Mouser:轻量开源的罗技鼠标驱动替代方案 Claude Opus 4.7:优缺点与评测信息汇总 openFuyao NPU-Operator故障排查 openFuyao 2603 共测测试报告 openFuyao InferNex AI推理集成部署 310P(300I Pro) 环境问题记录及解决 ceph mon Operation not permitted 问题解决 Ascend 310P + openFuyao + NPU-Operator 故障排查 KDE Plasma6 禁用全局菜单,恢复正常应用菜单 终极指南:在 Linux 裸机服务器上快速部署 Moltbot (原 Clawbot) 并集成飞书 Windows 配置 Claude Code 解决 settings.json 不生效 Windows 配置 Claude Code 全流程 2025-12-31 | 年终总结 AI 生图精品提示词|第二期:城市星球 AI 生图精品提示词|第一期 Kubernetes kubectl --raw 使用指南 彻底解决阿里云和 tailscale 冲突 2025-10-21 | 沉淀思维 macOS 单独为鼠标或触控板开启自然滚动 2025-10-16 | 负载高低 2025-10-15 | 睡眠周期 2025-10-14 | 转换情绪与独立观点 go 拉取 gitcode.com 私有 mod Git 将某个文件恢复到其他分支的状态 SSH 通过跳板机连接 lxc 使用 chronyc 构建 ntp 服务 2025-10-13 | 独立思考于未来能源 2025-10-12|AI Review 及高质量工作流 2025-10-11 | 人生阶段 2025-10-10 | AI Logo | 远程操控 AI 2025-10-09 | 去班味|银杏|域名 2025-10-08 2025-10-07 2025-10-06 2025-10-05 优秀的 SubReddit 清单 Synology 群晖重置 Syncthing 密码 让 LLM 看到真实世界的 Playwright MCP 磁盘占用分析利器 ncdu 自建 gitlab 徽标问题导致项目 500 问题解决 harbor Chartmuseum helm 图表缓存刷新 grep exclude 排除 Debian 11 + PVE LACP Mac 冲突问题调查与解决方案 Claude Code 实用技巧 CentOS 7 重置 root 密码 telnet 如何退出 kubernetes 的挂载传播(mount propagation)机制 vim 将命令输出到当前位置 harbor 替换 ssl 证书 AI提效之使用 cherry-studio + k8sgpt 实现 AI 巡检 k8s Claude Code Subagents 快速开始 《我看见的世界:李飞飞自传》 Lyra - AI Prompt Optimization Specialist Linux 自签名 CA 证书安装方法 超级玛丽、魂斗罗等经典小游戏聚合站 | PlayUnb 个人业余开发项目清单 快速选出收益最高的理财产品 | finance-calculator 苹果液态玻璃风格图像生成和模拟器 | LiquedGlass.icu Linux 进程绑定NUMA节点或CPU核心 判断GPT是否降智的几个问题 Harmony next hap 安装 名侦探柯南贝尔摩德出场集数 k3s k8s 快速部署轻量节点监控方案 beszel k3s-k8s 实现 DevOps 方案横向对比 k8s 配置访问私有镜像仓库 GoAccess 分析多网站日志方法 Octant - 以开发人员为中心的开源 Kubernetes Web 界面 Tailscale 自建 DERP 并配置 SSL 完整教程 OpenManus 使用记录 Plausible 缺失 location 信息的研究 解决 Nginx Ingress returns 413 Entity Too Large 绘图模型效果对比之城市气象 【转】k8s 认知路线 OpenFOAM 两大分支的详细比较 第一个 CUDA 程序之矩阵运算计算效能对比 Archlinux KDE Apache JMeter 配置高分屏缩放 解决 gitlab-runner 移除残留文件 permission denied nginx-ingress 配置路由 302 k8s 触发 pod 重新拉取镜像平滑升级的方法 Clickhouse 迁移后 permission denied 问题解决 Linux CPU 运行模式及功耗分析 Linux vim vi 翻页跳转命令快捷键 git 拉取所有 branch 和 tag 到本地并推送到远程 Rails 性能分析工具 rack-mini-profiler 和 bullet 全球国家、城市、地区开源数据库 Rails Active Record 常用命令 Rails Rake 简介与编写 如何调试 Vim 脚本 名侦探柯南松田阵平出场集数(最新1069话) 名侦探柯南妃英理出场集数(最新1069话) 《名侦探柯南》官方人气投票结果 名侦探柯南TV安室透出场集数(最新1110话) 名侦探柯南TV赤井秀一出场集数(更新至1194话) [转]名侦探柯南TV主线集数整理(截止1165集) 架构风格总结 [转译] 如何成为一个好的开发者? 【影评】名侦探柯南·零的执行人
《普林斯顿计算机公开课》
Tianlun Song · 2024-06-30 · via Oskyla 烹茶室

本文 首发于 🌱 煎茶转载 请注明 来源

仿佛对计算机的理解更深了一点; 好想去计算机博物馆看看。

摘录了很多内容,解决了我许多问题。

印象深刻

计算机协议中最重要的就是 IP 和 TCP,一个负责通信,一个负责传输。

摘录笔记

《普林斯顿计算机公开课(原书第2版)》 布莱恩·W. 柯尼汉 373个笔记

◆ 前言

云计算的快速应用增加了另一层复杂性。通过云计算,个人和公司在亚马逊、谷歌和微软等公司的服务器中存储数据并进行计算。数据不再由它们的所有者直接持有,而是被第三方直接掌握,这些第三方有着不同的规程、责任和漏洞,而且可能面临着相互冲突的司法规定。

将一切都连接到互联网的趋势将会持续,因为相互连接的好处有目共睹。然而不幸的是,这其中存在着很大的风险,因为有些设备不仅控制着我们的娱乐,还控制着生死攸关的系统,而且它们的安全性通常比更成熟的系统要弱得多。

政府不希望个人、公司或恐怖分子可以拥有真正的私有通信。因此,时常会有议案要求在加密算法中提供后门,允许政府机构破解加密。当然,这些仅在有着适当的保护措施以及为了国家安全利益的前提下才是有效的

关于计算机,一个作为未来总统的人应该了解什么?一个见多识广的人应该了解什么?你又应该了解什么?我认为有四个核心技术领域:硬件、软件、通信和数据。

◆ 第一部分 硬件

现代意义上的计算始于19世纪中期的英国,源于查尔斯·巴贝奇(Charles Babbage)的工作。巴贝奇是一位对航海和天文学感兴趣的科学家,这两门学科都需要用数值表来计算位置。巴贝奇一生中的大部分时间都在尝试制造计算设备,以便将创建表格甚至打印表格所需的烦琐且容易出错的手工计算机械化。通过前面的引言,你可以感觉出他对于计算的烦恼。由于各种各样的原因,包括疏远了他的财务支持者,他没能成功实现自己的雄心,但他的设计是合理的

艾达·洛芙莱斯通常被认为是世界上第一个程序员,Ada编程语言也以她的名字命名(见图I.2)。

第一个完全由电器组成的计算机是ENIAC(Electronic Numerical Integrator and Computer,电子数字积分计算机)。ENIAC于20世纪40年代由普瑞斯柏·埃克特(Presper Eckert)和约翰·莫克利(John Mauchly)于位于费城的宾夕法尼亚大学建造

计算设备可以将操作指令和数据以相同的方式存储,但是ENIAC并没有将指令和数据都保存在内存里。相反,它通过利用开关设置连接和重新布线进行编程

。第一台真正将程序和数据存储在一起的计算机是在英国建造的,最著名的是EDSAC(Electronic Delay Storage Automatic Calculator),即电子延迟存储自动计算器,于1949年在剑桥建成。

计算机的体系结构几十年来都大体不变,而硬件则以一种令人吃惊速度发生着改变

我们至少可以从两个角度来看待一台计算机:第一,逻辑或功能组织——有哪些部件,它们用于做什么以及它们是如何连接的;第二,物理结构——各部分的外观以及它们的制造方式

我的第一辆车是一辆很好用的产于1959年的大众甲壳虫,它和法拉利之间有天壤之别,但无论甲壳虫还是法拉利,都能将我和我的杂货从商店带回家,或者带我穿越整个国家。如此道来,它们的功能是相同的。

这是经济学家所说的网络效应的典型例子:当一样东西被其他人使用得更多时,它对你来说就更有用,其有用程度与使用者数量大致成正比。

一个处理器,一些主存储器,一些二级存储器,以及各种其他组件。这些组件都由一组叫作总线(bus)的电线连接起来,总线在它们之间传递信息

基本结构——也就是处理器、存储指令及数据的内存和存储设备、输入和输出设备——自从20世纪40年代起就已经形成规范。它通常被称为冯·诺依曼架构,以约翰·冯·诺依曼命名

处理器只能执行有限的基本操作,但它的执行速度惊人,达到每秒数十亿次。它可以根据之前的计算结果决定下一步要做什么操作,因此它在很大程度上独立于人类用户

处理器速度是根据它可以在一秒钟内完成的操作或指令(或其中部分)的数量来衡量的,至少近似如此

处理器使用内部时钟来一步步地执行其基本操作,就像心跳或时钟滴答一样。速度的一种衡量标准是每秒这种滴答声的数量。每秒的一个节拍被称为赫兹(Hz),是以德国工程师海因里希·赫兹(Heinrich Hertz)的名字命名的

无线电台的广播频率以兆赫(MHz,百万赫兹)为单位,

我那相当普通的2.2GHz处理器正在以每秒22亿次的速度运行着

兆是100万,也就是106;吉是10亿,也就是109。

主存储器被称为随机存取存储器(RAM),因为处理器可以快速访问存储在其中任意位置的信息,简单来说就是以随机顺序访问内存位置并不会减缓速度

大多数RAM是易失性的,也就是说,如果电源关闭,它的内容就会消失,并且所有当前活动的信息都会丢失。这就是为什么要谨慎地经常保存你的工作,特别是在台式机上,在使用台式机时踢掉电源线可能引起一场真正的灾难

那么容量是指什么?我现在使用的笔记本电脑有80亿字节(也可以称之为8千兆字节,或8GB)的主存,可能还是太小了

二级存储器即使在断电时也能保存信息。二级存储器主要有两种:较旧的磁盘称为硬盘或硬盘驱动器,较新的形式称为固态驱动器(SSD)

在最常见的英语文本表示中,一个字节可以存储一个字母字符

在文件系统这一例子中,无论不同的技术实际是如何工作的,内容都以文件和文件夹的层次结构呈现给用户。

。查尔斯·佩措尔德(Charles Petzold)的著作《编码的奥秘》(Code)对此做了很好的介绍,同时有许多网站也提供了图形动画以展示逻辑电路是如何执行算术和其他计算的

逻辑门构建在集成电路(Integrated Circuit, IC)上,通常被称为芯片或者薄芯片。集成电路把一个电子电路的所有元件和电路汇集在单一平面的电路(薄硅片)上,通过一系列复杂的光学和化学过程制造出没有分立部件和传统导线的电路

集成电路的制造依赖于硅,这使加州旧金山南部地区得到了硅谷的绰号,因为这里是集成电路产业最初开始的地方。它现在是该地区所有高科技企业的简称,也是纽约硅巷(Silicon Alley)和英国剑桥硅芬(Silicon Fen)等众多地区仿效命名的灵感来源。

。利用很少的数据点进行推算,摩尔发现随着科技进步,特定大小的集成电路内可以制造并安装的晶体管每年都翻一倍,这个频率他后来修改为每两年,也有人设为18个月。

这种指数增长,即现在所说的摩尔定律,已经持续了近60年,所以集成电路现在拥有的晶体管数量是1965年的100多万倍

摩尔定律不是自然法则,而是半导体业界用来设定目标的指南。从某种意义上来说这个定律会有失效的一天。

通过绘制气压随时间的变化,可以很容易地将声音可视化

“世界上只有10种人,理解二进制的和不理解二进制的。”

二进制很笨重,比十进制长三倍多,所以通常使用一种称为十六进制的替代记数法

每个十六进制数字代表4个位,

为什么用二进制而非十进制?答案很简单,制作只有像开与关这样两种状态的物理设备比有着10种状态的设备容易得多。这种相对简单性在许多技术中都得到了利用:电流(是否流动)、电压(高或低)、电荷(是否存在)、磁性(北向或向南)、光(亮或暗)、反射率(有光泽或暗淡)。冯·诺伊曼清楚地意识到了这一点。1946年,他说:“我们的基本内存单元自然地适合采用二进制,因为我们不试图测量电荷的逐渐变化。”

例如,唐纳德·克努斯(Donald Knuth)的《计算机程序设计艺术》第2卷描述了14世纪英国的酒器单位,分为13个二进制量级:2吉耳(gill)是1超品(chopin),2超品是1品脱(pint),2品脱是1夸脱(quart),依此类推,直到2桶(barrel)是1豪格海(hogshead),2豪格海是1派普(pipe),2派普是1坦恩(tun)

处理器还控制着计算机的其他部分,它使用总线上的信号来控制和协调任何与它连接的输入和输出,包括鼠标、键盘、显示器和其他任何组件

最重要的是,它可以做出决定,尽管是简单的决定:它可以比较数值(这个数比那个数大吗?)或者比较其他数据(这条信息与那条信息一样吗?),还能根据结果决定接下来做什么

这意味着处理器能做的虽然和计算器差不多,但它无需人的干预就可以完成工作

正如伯克斯、戈德斯坦和冯·诺依曼所说,“要让这种机器完全自动化,即让它在计算开始后不再依赖人工操作”。

每个内存位置都存有一个数字或一个指令,因此一个程序由存储在内存中的指令序列和数据项组成。运行时,处理器从第一个内存位置开始,重复一个简单的循环:获取:从内存中取得下一条指令译码:弄清楚该指令要做什么执行:执行指令,返回“获取

幸运的是,真有这种玩具计算机。图3.2显示了其中一个运行时的例子,它是一个用JavaScript编写的模拟器,以便在任何浏览器中运行,我们将在第7章中看到

真正的处理器也执行同样的“取指令—译码—执行”循环,只不过为了加快处理速度,还会配备精心设计的各种机制。

真正的计算机拥有比我们的玩具计算机多得多的指令,但这些指令的基本类型相同。它们有更多的移动数据的指令,更多的完成算术运算以及操作不同大小和类型数值的指令,更多的比较和分支的指令,以及控制计算机其他组件的指令

典型的处理器有几十到数百个不同的指令;指令和数据通常占用多个内存位置,通常为2~8个字节

真正的处理器有多个累加器,通常是16或32个,所以它可以在速度极快的内存中保存多个中间结果

计算机体系结构是研究处理器及其与其他计算机组件连接的一门学科;在大学里,它通常是计算机科学和电子工程的交叉领域

计算机体系结构考量的一个问题是指令集,也就是处理器配备的指令表

还是引用冯·诺依曼的话:“一般来讲,算术单元内在的经济性取决于期望的机器运行速度……与期望计算机的简单性或低价位之间的权衡。”

处理器非常快,通常执行一条指令只需要零点几纳秒。(回忆下,1纳秒等于十亿分之一秒,或者10-9秒。)相对而言,内存的速度则慢得让人难以忍受——从内存中取数据或指令大概要10~20纳秒

假如处理器不必等待数据到达,那它可能早就执行完数十条指令

随着集成电路特征尺寸变得越来越小,可以在一个芯片上封装更多的晶体管,而这些晶体管往往被用于更多的内核和更多的缓存存储器。单个处理器的速度并没有提高,但由于内核的增加,有效计算速度仍在不断提高

“因此,我们被迫认识到可能要构建一个层次性的存储,每一个都比前一个有更大的容量,但访问速度更慢。”——亚瑟·W.伯克斯、赫尔曼·H.戈德斯坦和约翰·冯·诺依曼,《电子计算机逻辑设计初探》,1946年

处理器中的多个累加器本质上也是一种缓存,只不过是高速缓存而已。内存也可以视为磁盘的缓存,而内存和磁盘又都可以视为网络数据的缓存。计算机网络经常会利用缓存加速访问来自远程服务器的信息流,而服务器本身也有缓存。

网站top500.org每六个月就重新公布一次全世界最快的500台计算机

超级计算机的速度是由每秒可以进行的浮点运算的次数,或者称为flops,来衡量的,也就是它们每秒可以对带有小数部分的数字进行的算术运算次数

分布式计算是指很多更加独立的计算机——它们不共享内存,比如,它们在地理上更加分散,甚至位于世界的不同地方

大规模的Web服务——搜索引擎、在线商店,社交网络,以及云计算——都是分布式计算系统。

对于非专业人员,图灵的手段最容易理解。他描述了一个非常简单的计算机,比我们的玩具计算机还简单,展示了它可以计算任何一般意义上可计算的东西。他描述的这种计算机,我们今天叫作图灵机。然后,他展示了如何创建一种可以模拟任何其他图灵机的图灵机;这种图灵机现在被称为通用图灵机

写一个模拟通用图灵机的程序很容易,写一个程序让通用图灵机模拟真实的计算机也是可能的(尽管不容易)。因此,从能够做什么计算任务的角度上讲,所有计算机都是等价的,尽管运行速度上明显不同

图灵的战时工作已经在几部电影中出现,有相当多的艺术授权,包括1996年的《破译密码》和2014年的《模仿游戏》

“没有必要设计各种新机器来完成各种计算过程。它们都可以用一台数字计算机完成,并为每种情况进行适当的编程。”——艾伦·图灵,《计算机器与智能》,Mind,1950年

◆ 第二部分 软件

计算机自己不会做任何事情,除非有人极其详细地告诉它该做什么。计算机是魔法师的好学徒,能够不知疲倦地遵循指令而不出错,但需要极其精确给出关于具体如何做的说明。

软件是指令序列的总称,这些指令序列能让计算机做一些有用的事情

与“硬”的硬件相比,它是“软”的,因为它是无形的,不能用手触摸到。硬件是有形的,如果你的笔记本电脑掉下来砸到脚上,你会立刻有反应。但对软件来说则不是那么回事了

我的斯巴鲁森林人(Subaru Forester)牌汽车有两个摄像头,可以看到前挡风玻璃。如果我在没有打信号的情况下换道,或者当一辆车或一个人看起来离我太近时,它会利用计算机视觉向我发出警告。它经常出错,频繁地发出干扰性误报,但它也救了我几次

费曼算法:1.把问题写下来。2.思考真正的困难。3.写下答案。——物理学家默里·盖尔曼,1992年

解释软件是什么的一个通俗的比方是菜谱。菜谱会列出做某道菜所需的原材料、烹饪步骤以及预期结果。类似地,程序也要描述待操作的数据,讲清楚要对数据做什么,以及产出什么结果

不过,菜谱比任何程序都含糊不明,容易产生歧义。所以这个比喻并不是非常恰当

例如,巧克力蛋糕的食谱上写着:“在烤箱中烘烤30分钟或直到它凝固,将你的手掌轻轻放在蛋糕表面上进行测试。”测试人员从中可以读到什么?——摆动,阻力,或者其他什么东西?“轻轻”有多轻?烘焙时间应该至少30分钟还是不超过30分钟

算法效率,即计算时间与要处理的数据量之间存在什么关系

如果计算时间与数据量成正比或线性比例,那该算法就称为线性时间算法或就是线性算法。

二分查找的关键是数据量的增长只会带来工作量的微小增长

计算机科学这个领域花了多年时间来细化“我们能计算多快”的概念。使用如N、log N、N2或N log N的数据量来表示运行时间,是对这些思考结果的升华

排序是一个N log N问题,但快速排序是一个N log N算法,而选择排序则是一个N2算法。

算法是一个精确而没有歧义的“菜谱”。它是用一组确定的基本操作来表达的,这些操作的含义是完全已知并且明确的。算法列出了使用这些操作的一系列步骤,涵盖了所有可能的情况,并确保算法最终会停止

程序绝不是抽象的,它是对真正的计算机为了完成一项任务必须执行的每一步的具体表述。算法和程序之间的区别就像图纸和建筑物之间的区别:一个是理想化的,另一个是具体存在

程序必须考虑实际的问题,比如内存不足、处理器速度不快、无效或恶意的输入数据、错误的硬件、网络连接中断,以及(在幕后,却经常会导致其他问题恶化的)人性的弱点

如果说算法是理想化的菜谱,那程序就是让烹饪机器人冒着敌人的炮火,为军队准备一个月的食物的详细指令集

这个强大的想法——用一个程序操纵另一个程序——一直是软件领域重大进步的核心

在实际情况下,编译器内部可能划分成一个“前端”和多个“后端”。“前端”负责把高级语言的程序转换为某种中间形式,而“后端”则负责把中间表现形式转换成针对特定体系结构的汇编指令。这种组织方式要比使用多个完全独立的编译器更简单

最早的一门语言之一叫作FORTRAN,这个名字来源于“公式翻译”,现在写成“Fortran”。Fortran是由约翰·巴库斯(John Backus)领导的IBM团队开发的,在科学和工程计算方面非常成功。许多科学家和工程师(包括我)学习的第一门编程语言就是Fortran。

COBOL是专门针对商业数据处理的语言,其语言特征非常适合表达管理库存、准备发票、计算工资等方面的数据结构和计算

COBOL现在也有人在使用,虽然发生了很多变化,但仍然能看出其特点。有很多遗留的COBOL程序,但COBOL程序员不多。2020年,新泽西州政府发现,他们处理失业申请的古老程序无法应对新冠肺炎造成的申请数量增加,但该州无法找到足够多有经验的程序员来升级COBOL程序

BASIC当初的设计目标是要成为教授编程的简易语言。它特别简单,只需要非常有限的计算资源,因此也成为第一批个人计算机上可用的第一门高级语言。事实上,微软公司的创始人比尔·盖茨和保罗·艾伦的发迹,也是始于为1975年的Altair微型计算机编写BASIC编译器,这个编译器是微软公司的第一个产品。今天,Microsoft Visual Basic作为BASIC的一个主要分支,仍然由微软公司积极地维护

在计算机价格昂贵、速度又慢而且功能有限的时期,人们担心用高级语言写出来的程序效率太低,因为编译器生成的汇编代码远不如一个熟练的汇编程序员写得精简而高效。编译器作者付出了很大努力,使得生成的代码能够达到手写代码一样好,而这有助于高级语言的流行

FORTRAN、COBOL和BASIC获得成功的部分原因,是它们都专注于某个特定的应用领域,而且有意不去试图处理所有可能的编程任务

20世纪70年代,出现了专门为“系统编程”开发的语言。所谓系统编程,就是编写汇编器、编译器、文本编辑器乃至操作系统等程序员使用的工具。迄今为止,这些语言中最成功的是C,由丹尼斯·里奇(Dennis Ritchie)于1973年在贝尔实验室开发,至今仍然是最流行和广泛应用的编程语言之一。

20世纪80年代C++语言问世,C++语言由比雅尼·斯特劳斯特鲁普(Bjarne Stroustrup)同样在贝尔实验室开发,定位是应对大型程序开发过程中的复杂性

今天,我们在计算机中使用的主要软件都是用C或C++编写的。我写这本书所用的Mac,其中安装的大多数软件都是用C、C++和Objective-C(C的一种方言)写的。我最开始的草稿是用Word写的,Word也是使用C和C++语言编写的程序;今天,我使用C和C++程序编辑,格式化并且打印,备份则放在UNIX和Linux(都是C程序)操作系统上,而我上网使用的是Firefox、Chrome和Edge(都是用C++写的)

20世纪90年代期间,随着互联网和万维网的发展,更多语言被开发出来。计算机处理器的速度不断加快,内存容量也不断增大,而编程是否高效和便捷变得比机器效率更重要;此时诞生的Java和JavaScript就是做了这些方面的权衡

20世纪90年代初,詹姆斯·高斯林(James Gosling)在Sun Microsystems公司开发了Java。Java最初的目标是开发小型嵌入式系统,例如家用电器和电子设备中的系统,因此对速度要求不高,但对灵活性的要求很高

从这个意义上说,编程就像是文学创作。风格以及恰如其分地运用语言对写作至关重要,对写程序同样至关重要,而且还是区分真正伟大的程序员与普通程序员的标志

JavaScript同样是C衍生语言大家族的一员,但它与C的差别非常大。它是布兰登·艾奇(Brendan Eich)于1995年在网景公司开发的。除了共享部分名称外,JavaScript与Java没有任何关系。

JavaScript可以很方便地进行实验。这门语言本身也简单。你不需要下载编译器;每个浏览器都内置了一个。你的计算结果可以立刻展现。我们很快就会看到,给这个程序添上几行代码,然后把它放到网页中,世界上的所有人就都可以使用它了

Python由吉多·范罗苏姆(Guido van Rossum)于1990年在阿姆斯特丹的荷兰国家数学和计算机科学研究学会(Centrum Wiskunde & Informatica, CWI)开发并推出。它在语法上与C、C++、Java和JavaScript有些不同,最明显的一点是,它使用缩进来指示语句如何分组,而不是花括号

以后的语言将何去何从?我猜想,我们将通过使用更多的计算机资源让编程变得更容易。而且我们还会继续发展那些对程序员来说更安全的语言

比如,C语言就像一种非常锋利的工具,在使用时很容易在无意中犯一些编程错误,而等到被发现时已为时已晚,也许是在它们被恶意利用之后。用较新的语言更容易防止或至少能检测到某些错误,虽然有时其代价是运行速度变慢或占用更多内存

大多数时候,取舍的方向是正确的;然而仍然有很多应用程序——例如汽车、飞机、宇宙飞船和武器上的控制系统——紧凑、快速的代码很重要,所以像C这样高效的语言仍然会被使用

虽然所有语言在形式上都是等价的,因为都可以用于模拟图灵机或者被图灵机所模拟,但这绝不是说它们都适用于所有的编程任务

写一个控制复杂网页的JavaScript程序,与写一个实现JavaScript编译器的C++程序仍有天壤之别。同时可以被称为这两种编程任务专家的程序员并不多见,经验丰富的专业程序员也可能熟悉或略懂十几门语言,但他们不会对多种语言都同样精通

每种语言都代表了对效率、表达能力、安全性和复杂性的一种权衡考虑

正如美国语言学家本杰明·沃尔夫(Benjamin Whorf)所说:“语言塑造我们的思维方式,决定我们可以思考什么。”

根据谷歌在2015年的一次会议报告,谷歌总共有大约20亿行代码;而现在可能至少是这个数字的两倍了。

所有重要的程序几乎没有从零开始写的,有许多别人已经写好的组件可以拿来直接用。举个例子,如果你在为Windows或Mac写程序,那么有很多库都能提供预制的菜单、按钮、图形计算、网络连接、数据库访问功能等。你的主要工作是理解这些组件,然后再以自己的方式把它们“粘”在一起。当然,这些组件反过来又依赖于其他更简单和更基本的组件,通常分成几层。而在最下层,所有程序运行在操作系统之上,它是负责管理硬件并确保一切井然序的程序

函数库提供的服务是采用应用程序编程接口(Application Programming Interface, API)向程序员描述的,API会列出所有函数,说明其用途,如何在程序中使用它们,需要的输入数据,以及生成什么值。API也会描述数据结构,也就是来回传递的数据的组织形式,和其他各种各样的片段,它们一起定义了程序员请求服务需要做什么,以及计算将返回什么结果

这种说明书必须详细而且精确,因为基于它编写的程序最终会由一台不会说话的计算机而不是一个随和友善的人去解读。

1947年,霍普的同事在哈佛MarkⅡ(他们当时使用的一种机械计算机)中发现了一只虫子(死了的蛾子),她就说他们是在给这台机器“除虫”(debug)。那只死虫子后来被保存下来,还做成了标本供后人参观。你可以在华盛顿的史密森尼美国历史博物馆里看到它,

1889年《蓓尔美街报》,3月11日,1/1我听说啊,爱迪生先生连续两个晚上都在找他留声机里的“bug”——其实就是在排除故障,但听起来好像所有问题都是因为有个虫子偷偷钻进去引起的

软件中的漏洞通常允许对手用自己的恶意代码覆盖内存,从而使系统容易受到攻击

关于可利用的漏洞有一个活跃的市场:白帽解决问题,黑帽利用问题,中间有一个灰色地带,像美国国家安全局这样的政府机构会利用库存漏洞,稍后再使用或修复。

必须持续不断地稳步更新,这是软件开发和维护的一大问题,但是不得不这样做。否则,程序就会遭遇“比特腐烂”,一段时间之后,也许就不能用了,或者无法更新,因为重新编译无法通过,或者它所依赖的库已经变化太大。与此同时,修复已有的问题或者添加新功能有可能会造成新的程序bug或改变用户熟悉的行为。

与硬件相比,软件是一个比较新的领域;1950年之前还没有软件。软件成为经济发展的一个重要产业,还只是近四十年的事。作为结果,相关的法律、商业实践和社会规范等机制还来不及完善。

最初,软件——算法和程序——是不能申请专利的,因为它被认为是“数学”,故而不在专利法管辖范围内

亚马逊的“一键购买”(1-click)专利或许能作为软件专利的典型代表。1999年9月,美国第5960411号专利被授予Amazon.com的四位发明人,包括创始人和CEO杰夫·贝索斯(Jeff Bezos)在内。这项专利涵盖“通过互联网下订单完成购买的方法和系统”,所声明的创新之处是允许注册用户单击一次鼠标即可下订单购买(见图5.10)。顺便说明一下,“1-Click”是一个注册的亚马逊商标,用1-Click®表示。

“1-Click”专利成为了将近20年的争论或法庭辩论的主题。公平地讲,大多数程序员都会认为这个想法很显而易见,但法律规定的却是一项发明在发明当时应该对“具有一般专业技能的人”是“不显而易见的”。当时还是1997年,电子商务才刚刚萌芽。美国专利局拒绝了这项专利的一些说法,另外一些则仍在持续申请中。与此同时,该项专利已授权给其他公司,包括苹果的iTunes在线商店。亚马逊已取得强制令,禁止其他公司未经允许使用“一键购买”。当然,其他国家的情况有所不同。所幸,这在今天已经毫无意义了,因为专利的有效期是20年,现在已经过期了。

大多数EULA规定,如果软件对你造成了伤害,你不能就损害赔偿提起诉讼。软件的使用是有条件的,而且你必须同意不会对它进行逆向工程或者反汇编。你不能把它带到某些国家,也不能用它来开发核武器(的确是这样!)。我的律师朋友们说,只要相应的条款不是特别不合理,这种许可一般都是有效的,而且是可以强制执行的,这似乎引出了什么是合理的问题。

API的版权状况不是一个假设性的问题。2010年1月,甲骨文公司收购了发明Java编程语言的太阳微系统公司(Sun Microsystems),并在2010年8月起诉谷歌,指控谷歌公司在运行Java代码的Android手机上非法使用了Java API

标准是对某个工件如何构建或应该如何工作的精确而详细的描述。有些标准是事实标准,比如Word的.doc和.docx文件格式。它们不是官方标准,但每个人都在使用它们。“标准”这个词最好留用于正式的说明书中,通常是由准中立方(如政府机构或财团)开发和维护的,它们定义了事物是如何建造或运行的。该定义足够完整和精确,使得分离的实体可以交互或提供独立的实现

软件领域也有各种各样的标准,字符集有ASCII和Unicode,编程语言有C和C++,用于加密的和压缩的各种算法,还有通过网络交换信息的各种协议。

1983年,他发起了一个叫GNU(即“GNU’s Not Unix”,gnu.org)的项目,致力于开发一些重要软件(比如操作系统和编程语言的编译器)的免费和开放版本。他还创办了一个非营利组织,叫自由软件基金会(Free Software Foundation),目标是开发永远“自由”的软件,也就是说这些软件不是专有的、不会受到所有权的限制。自由软件是通过获得一个名为GNU通用公共许可证(General Public License, GPL)的独创版权证书来实现的。

GPL是一种强有力的许可,一些违反其条款的公司已经被禁止使用其代码,或者发布基于许可代码的源代码。

编程语言和支持工具现在几乎都是开源的;事实上,很难构建一种严格具有专利性的新的编程语言。在过去的十年里,谷歌创造并发布了Go,苹果创造并发布了Swift,Mozilla创造并发布了Rust,而微软则发布了C#和F#,这些都是多年来的专利

Linux操作系统或许是最广为人知的开源系统了,它被个人和大型商业企业广泛使用,比如谷歌的全部基础设施都运行在Linux之上

红帽发布的Linux源代码可以在网上免费下载,但公司通过支持、培训、质量保证、集成和其他服务可以赚取利润。

许多开源程序员本身就在那些使用并支持开源软件的企业工作,IBM、Facebook以及Google都是明显的例子,但绝非特例。微软现在是开源软件项目的最大贡献者之一。这些公司通过帮助引导开源软件的发展,通过让其他人修复bug和改进功能而获得收益。

并不是所有开源软件都能独领风骚,开源版本不如它所模仿的商业版本的情况也比比皆是。但是,对于一些核心的程序员工具和系统来说,开源软件的确无可匹敌

“程序员,就像诗人一样,几乎仅仅工作在单纯的思考中。他通过发挥想象力在空中建造他的城堡。很少有创作媒体如此灵活,如此容易精炼和重建,如此容易实现宏大的概念设想。”——弗雷德里克·P.布鲁克斯,《人月神话》,1975年

1969年,贝尔实验室的肯·汤普森(Ken Thompson)和丹尼斯·里奇(Dennis Ritchie)开始着手开发UNIX。他们曾开发过Multics系统,这是一个延续自CTSS,较之更完善但却不那么成功的系统。今天,除了微软开发的那些操作系统之外,大多数操作系统要么源自当初贝尔实验室的UNIX系统,要么是与UNIX兼容但独立开发的Linux版本。里奇和汤普森因为开发了UNIX而一起荣获1983年的图灵奖。

有效利用主存储器需要良好的工程技术。一种技术是在需要时仅将程序的一部分加载到内存,而在程序处于非活动状态时再把它转存回磁盘,这个过程称为交换(swapping)

处理器的构造是这样设计的:当计算机启动时,处理器首先执行存储在永久存储器中的一些指令。这些指令继而从一小块闪存中读出足以运行某些设备的代码。这些代码在运行过程中再从磁盘、USB存储器或网络连接的既定位置读出更多指令。这些指令再继续读取更多指令,直到加载了足够完成有效工作的代码为止。这个开始的过程最初被称为引导(bootstrapping),源于“自力更生”这个古老的表达,现在简称为启动(booting)

一旦操作系统运行起来,它就会转而执行一个简单循环,依次把控制权交给准备运行或需要关注的每个应用程序

操作系统以标准化的或者说协商一致的方式提供这些服务,而应用程序通过执行一种特殊的指令来请求这些服务,并将控制权移交给操作系统中特定的地址。操作系统根据请求完成计算,然后再将控制权和结果返回给应用程序。操作系统的这些入口被称为系统调用(system call),而对这些系统调用的详细规范实际上恰恰定义了操作系统是什么样子。现代操作系统通常有几百个系统调用

设备驱动程序是一种沟通操作系统与特定硬件设备(如打印机和鼠标)的程序。驱动程序的代码具有怎么让特定设备执行自己的工作的详细知识,比如从特定的鼠标或触摸板得到运动和按钮的信息、让磁盘通过集成电路或旋转的磁表面读写信息、让打印机在纸上留下标识、让特定的无线芯片发送和接收无线电信号

随着这种趋势的不断发展,选择现成的操作系统要比自己从头写一个来得更实际。除非用途特殊,否则在Linux基础上改一改是成本最低的,Linux非常稳定、容易修改、方便移植,而且免费。相对而言,自己开发一个专有系统,或者取得某个商业系统的许可,会需要很大成本。当然,改造Linux的缺点在于必须把改造后的操作系统部分代码按照GPL许可发布,由此可能引发如何保护设备中知识产权的问题。不过,从Kindle和TiVo等许多案例来看,这并不是不可逾越的障碍。

文件系统是操作系统的一个组成部分,它能够让硬盘、CD和DVD,以及其他移动存储设备等物理存储媒介,变成看起来像是由文件和文件夹组成的层次结构

文件系统是逻辑组织和物理实现之间区别的一个很好的例子

文件系统存储信息的方式可能具有实际甚至法律含义,因此研究文件系统的另一个原因是了解为什么“删除文件”并不意味着其内容永远消失。

[UNIX系统则一直使用目录(directory)而不是文件夹这一词汇

在支持多用户的系统中,还要保证信息的隐私权和安全性,不能让一个用户在未经允许的情况下访问另一个用户的文件,并且可能还需要限制每个用户有权使用的空间量

在底层,文件系统服务是通过系统调用来提供的。程序员通常要借助代码库来使用这些系统调用,以简化编程过程中常见的文件处理操作

一个500GB的硬盘包含5000亿个字节,但硬盘上的软件可能会将其表示为5亿个或每个1000字节的块

真实的计算机中,块的大小应该是2的幂

文件系统不会在同一个块内存储不同文件的信息,因而就免不了有一些浪费

这个文件所在的文件夹条目中会包含该文件的名字、2500字节的文件大小、创建或修改的日期和时间,以及其他各种相关信息(权限、类型等,取决于操作系统)。所有这些信息都可以通过资源管理器或者Finder看到

这个文件夹条目中还会包含文件在磁盘上的位置信息,也就是5亿个块中的哪个块存储着这个文件的字节

管理这些位置信息的方法有很多种。比如,文件夹条目可以包含一个块编号列表;也可以引用一个自身包含一个块编号列表的块;或者只包含第一个块的编号,第一个块又包含第二个块的编号,依此类推。

但是SSD设备有不同的驱动程序,设备本身有复杂的代码来记住信息在设备上的位置。这是因为SSD设备受限于每个部件的使用次数。驱动器中的软件跟踪每个物理块的使用情况,并移动数据,以确保每个块的使用大致相同,这一过程称为磨损均衡。

文件夹也是一个文件,其中包含着文件夹和文件的位置信息。由于涉及文件内容和组织的信息必须精准、一致,所以文件系统保留了自己管理和维护文件夹内容的权限。用户和软件只能请求文件系统来间接地修改文件夹内容

从某个角度来看,文件夹也是文件。从存储方式上讲,它们跟文件没有任何区别。只不过文件系统会全权负责管理文件夹内容,任何应用软件都不能直接修改该内容。但在最底层,它只保存在块中,都由相同的机制进行管理

实际场景中,应用程序和操作系统会追踪当前正在使用的文件夹,因此,文件系统不必每次都从根开始搜索,此外,为了加快处理速度,系统还可能会缓存频繁用到的文件夹

应用程序在创建新文件时会向文件系统发送请求,文件系统会在相应的文件夹中增加一个新条目,包含文件名、日期等项,还有文件大小为零(因为还没有为这个新文件分配磁盘块)

接下来,应用程序要向文件中写入某些数据时,比如向一封邮件中添加一些文本,文件系统会找到足够多的当前没有使用的或者“空闲”的块来保存相应信息,并把数据复制过去。然后把这些块插入到文件夹的块列表中,最后返回给应用程序。

这表明文件系统维护了驱动器上当前未使用的所有块的列表,也就是说,这些块不是某个文件的一部分。每当应用程序请求新磁盘块,它就可以从这些空闲的块中拿出一些来满足请求。这个空闲块的列表同样也保存在文件系统的块中,但只能由操作系统访问,应用程序是访问不到的

删除文件时,过程恰好相反:文件占用的块会回到空闲列表,而文件夹中该文件的条目会被清除,结果就好像文件被删除了一样。事实上,情况并非如此,其中蕴含着许多有趣的含义。

原始文件的每个块的所有字节仍然保持不变。它们不会被新内容覆盖,直到该块从空闲列表中删除并留给一个新文件。

这种延迟意味着,你认为已经删除的信息仍然存在,而且如果你知道如何找到它,就可以很容易地访问它。任何通过物理块读取驱动器的程序,也就是说,不经过文件系统层次结构,都可以看到原来的内容是什么

2020年中期,微软发布了Windows File Recovery(文件恢复),这是一个免费的工具,可以对各种文件系统和媒介进行这种类型的恢复

军用级文件擦除会用随机的1和0对要被释放的块进行多次覆盖。更好的方法是将硬盘放在强磁铁附近,使其退磁。最好的办法是在物理上摧毁它;只有这样才能确保里面的内容都没了。

一个简单但可能不够完美的方法,就是把任何确保一个应用程序不会干扰另外应用程序的代码看成是操作系统的一部分

应用程序完成任务,操作系统充当协调者和“交通警察”,以确保应用程序有效而公平地共享资源——处理器时间、内存、二级存储、网络连接和其他设备,并且互不干扰

如果你在经过一天的努力后,很难让10行代码正常工作,那么你可能会对那些声称具有百万行的程序将按时交付且没有bug的人表示怀疑

JavaScript也有缺点。语言的某些部分是笨拙的,会有一些令人惊讶的行为。浏览器界面并不像人们希望的那样标准化,因此程序在不同的浏览器上的行为并不总是相同的

任何编程语言都会提供一些方法来读取输入数据、进行算术计算、存储和获取中间值,并根据之前的计算结果决定下一个计算步骤,在计算过程中显示结果,以及在计算完成时保存结果

编程语言具有语法,而语法就是一系列规则,根据它们可以判断什么符合语法,什么不符合

计算机是一遍又一遍地重复指令序列的奇妙设备;问题是如何在编程语言中表达这种重复

如果你在网页上运行这个程序,测试它是很容易的,但专业的程序员甚至会在这之前就检查它,模仿它的行为,通过在心里一步一步地检查程序的语句,就像一台真正的计算机会做的那样

优秀的测试人员会认真考虑可能出现的错误,包括奇怪或无效的输入,以及“边缘”或“边界”的情况,比如根本没有数据或被零除。

没有完美的解决方案,但是仔细地进行程序设计和实现会有所帮助。比如从一开始就在程序中添加一致性和完整性检查的代码。这样,如果出现问题,很可能会被程序自身及早发现。

正如我们将在第11章看到的,Web的发展趋势是越来越多地使用JavaScript,包括像Maps这样的可编程接口。这样做的一个缺点是,当你被迫公开源代码时,很难保护知识产权,而如果你使用JavaScript,则必须公开源代码。任何人都可以使用浏览器查看页面的源代码。有些JavaScript代码是混淆过的,要么是有意的,要么是为了让它更紧凑,以便更快地下载,结果可能是完全无法破解的,除非有人下决心搞定

浏览器在遇到网页中的JavaScript代码时(比如遇到<script>标签时),就会把代码文本移交给JavaScript编译器

Google的Colab(colab.research.google.com)是最简单的。它提供了对各种机器学习工具的方便访问。我们不会在这里深入讨论机器学习,但Colab对于开始使用Python也很有用

Colab是一种被广泛使用的交互式工具——Jupyter Notebook的云版本,Jupyter notebook是一种基于计算机的物理笔记本的模拟,你可以在其中记录想法、解释、实验、代码和数据,所有这些都在一个网页中,可以被编辑、更新、执行和分发给其他人。更多信息可以在jupyter.org上获得

到目前为止,大多数的程序示例都是数字的,很容易认为编程就是将数字移来移去。但这当然不是真的,正如我们在生活中看到的所有有趣的应用都是非数字计算的

编译器检查程序的错误,并将其编译成汇编语言指令,在一个类似于玩具程序的模拟器中运行,当然它具有更丰富的指令集,如第6章所述的虚拟机

翻译方式有很多种,最常见的是使用编译器,有时候还要用汇编器,把用诸如C等语言编写的程序转换成二进制形式,以便在计算机上运行

不同的处理器有不同的指令集和指令形式,因此需要不同的编译器,尽管编译器的某些部分对不同的处理器来说是通用的

解释器和虚拟机是模拟真正或假想计算机的程序,可以面向它们编译并运行代码,这就是JavaScript和Python程序通常的运行机制。

接口。接口或者API(应用程序编程接口)是提供服务的软件与使用该服务的软件之间的一种约定。库和组件通过应用编程接口提供服务。操作系统通过系统调用接口使硬件看起来更规范且可编程

◆ 第三部分 通信

通信系统也是大多数社会问题产生的地方,带来了棘手的隐私、安全问题,以及个人、企业和政府之间的权利竞争问题。

大仲马出版于1844年的《基督山伯爵》中,第61章讲述了伯爵如何贿赂一个电报员,让他向巴黎发送一条假消息,导致邪恶的银行家腾格拉尔男爵破产。这是中间人攻击的完美例子

可能对电话系统过于偏爱,因为我在美国电话电报公司(AT&T)下属的贝尔实验室(Bell Labs)工作了30多年,就算没有接触很核心的东西,但还是作为内部人士看到了许多变化。另一方面,我确实怀念手机普及之前通话可靠、语音清晰的日子

华生先生,过来,我想见你。”——第一个通过电话发送的可理解的信息,亚历山大·格雷厄姆·贝尔,1876年3月10

带宽(bandwidth)是任何网络最基本的属性,即网络传输数据的速度

延迟(latency或delay)度量特定信息块通过系统所需的时间。高延迟并不意味着低带宽:在全国各地驾驶装满磁盘驱动器的卡车有高延迟,但带宽巨大。

抖动(jitter)是指延迟的可变性,这在一些通信系统中也很重要,尤其是那些处理语音和视频的系统。

范围(range)定义了在给定技术下网络在地理位置上的大小。一些网络是局部的,最多只有几米,而另一些则跨越了世界。

对大多数人来说更重要的是,通过电话发送信息的速度被严格限制,最高速度约为56Kbps(每秒56000比特——小写的“b”通常代表比特,而大写的“B”代表字节),即每秒7KB。因此,下载一个20KB的网页需要3秒,下载一个400KB的图片需要近60秒,而视频或软件更新可能需要数小时甚至数天。

还有另一个复杂问题:有线电视网络是单向的,它能把信号广播到所有家庭,这很容易建设,但没有办法把信息从客户发回给有线电视公司。有线电视公司无论如何都要想办法解决这个问题,因为视频点播收费和其他服务需要从用户那里接收信息。于是有线网络就变成了双向的,这就为利用有线网络来实现计算机之间的数据通信提供了条件。然而,上传速度(从消费者到有线电视公司)通常比下载速度低得多,因为大部分流量都是用来下载的。

另一种用于家庭的相当快的网络技术是基于家庭中已经存在的另一种系统,即老式电话。它被称为数字用户线路(Digital Subscriber Loop, DSL),有时是ADSL,A表示“不对称”(asymmetric),因为往下到户的带宽高于向上出户的带宽。它提供的服务与有线电视基本相同,但在底层机制上有很大的不同

DSL通过一种不会干扰语音信号的技术在电话线上发送数据,因此你可以一边上网一边打电话,而不会相互影响。这种方法效果很好,但只在一定的距离内有效

资源的争用可以通过一种巧妙的技巧来处理:如果一个网络接口开始发送,但检测到其他人也在发送,它会先停下来,等待一小段时间,然后再次尝试发送。如果等待时间是随机的,并在一系列失败后逐渐增加等待时间,那么最终一切都会搞定

为什么称其为“蜂窝”呢?这是由于频谱和无线电的覆盖范围都是有限的,因此一个地理区域被划分为蜂窝状的许多“单元”。

手机流量很容易受到一种被称为黄貂鱼(stingray)的设备的定向攻击,该设备得名于一种名为StinGray的商业产品。黄貂鱼模仿手机发射塔,以便附近的手机与该设备通信,而不是与一个真正的发射塔通信。这可以用于被动监视手机,或主动与手机进行接触(中间人攻击)。手机被设计用来与提供最强信号的基站通信,因此,黄貂鱼在一个小的范围内工作,在这个范围内,它能发出比附近任何信号塔都强的信号。

LO——第一条阿帕网信息,1969年10月29日从加州大学洛杉矶分校发送到斯坦福大学。本应该发送LOGIN(登录),但系统崩溃了

并不是一个巨型网络,更不是一台巨型计算机。它是一个松散、非结构化、混乱、自组织的网络集合,这些网络由定义了网络以及其中的计算机之间相互通信时所遵循的标准连接在一起。

今天,互联网由数百万个松散连接的独立网络组成。临近的计算机由局域网连接,通常是无线以太网。网络又通过网关或路由器与其他网络相连,网关或路由器是专门用于将信息包从一个网络路由到另一个网络的计算机。(维基百科上说网关是通用设备,而路由器是专用的,本书不做区分,通称为“网关”。)网关之间互相交换路由信息,这样它们就至少知道哪些网络与本地网络相连,因此可以被访问到。

在互联网上,数据以IP(即Internet Protocol,互联网协议)包的形式被携带。所有IP包的格式相同,在任何特定的网络上,一个IP包可以在一个或多个物理包中传输。例如,一个大的IP包将被分成多个较小的以太网包,因为以太网包可能的最大大小(约1500字节)比最大的IP包(约65000字节)小得多

每个IP数据包都要经过多个网关,每个网关将数据包发送到一个更接近最终目的地的网关。当一个数据包四处穿梭时,它可能要经过20个网关,这些网关由10多个不同的公司或机构拥有和运营,并且可能位于不同的国家。数据流量不需要遵循最短的路径,考虑方便和成本因素,可能使数据包通过较长的路由。许多源地址和目的地址在美国以外地区的数据包使用的是经过美国的电缆,美国国家安全局利用这一点来记录全球通信。

IPv4地址只有232个,约43亿。这样地球上平均每人分不到一个地址,所以从人们使用的通信服务数量来看,一些东西将会耗尽。事实上,情况比听起来更糟,因为IP地址是按块分配的,因此使用效率不高(普林斯顿大学真有131000台计算机同时在工作吗?)。无论如何,除了少数例外,世界上大部分地区的IPv4地址都已分配完毕。

将多个主机连接到单个IP地址的技术提供了一些缓解的余地。家庭无线路由器通常使用网络地址转换(Network Address Translation, NAT),一个外部IP地址可以服务于多个内部IP地址

如果你有一个NAT设备,你所有的家庭设备在外部都显示着相同的IP地址;位于设备中的硬件和软件处理着内外地址的双向转换

一旦全世界都转向使用128位地址的IPv6,这种地址短缺的压力就会消失——大约有2128或3×1038个地址,所以我们不会在短期内用完

互联网网关通过与邻近网关交换信息来持续刷新自身的路由信息,这样就能保证可能的以及理想的路径信息基本都是最新的

互联网的庞大规模要求采用分层结构来管理路由信息。在路由系统的最顶层,几万个自治系统提供了它们所包含的网络的路由信息。一个自治系统通常也对应于一个大的互联网服务提供商(ISP)。在单个自治系统内部,路由信息仅进行本地交换,但整个自治系统对外部展示统一的路由信息。

ISP之间通过网关相互连接。由于主要运营商之间的网络流量巨大,所以多个公司的网络都汇聚到运营商的互联网交换中心(Internet exchange points, IXP),运营商网络之间则互相建立物理连接

大型交换中心以每秒传递TB量级的速度将数据从一个网络传到另一个网络;例如,DE-CIX法兰克福交换中心是世界上最大的交换中心之一,目前的平均交换流量接近6Tbps,峰值超过9Tbps

从不同网关中名称的神秘缩写去判断它们的位置是很有趣的。从一个国家连接到另一个国家也可以很容易地通过第三方国家的网关进行,这第三方国家通常会包括美国;这个事实可能很令人惊讶,而且不受欢迎,这取决于传送信息的性质和所涉及的国家。

不幸的是,随着时间的推移,traceroute程序能提供的信息量越来越少,出于安全方面的考虑,越来越多的站点选择不向traceroute程序提供运行所需要的信息。例如,一些网站不透露其名字或IP地址

互联网有很多协议,其中最基础的有两个,一个是互联网协议(Internet Procotol, IP),定义了单个包的格式和传输方式,另一个是传输控制协议(Transmission Control Protocol,TCP),定义了IP包如何组合成数据流以及如何连接到服务。两者合起来起就叫TCP/IP

网关对IP数据包进行路由,尽管每个物理网络都有自己的IP数据包传输格式。每个网关必须在数据包进出时在网络格式和IP之间进行转换

在IP层之上,TCP确保可靠通信。这样,用户(实际上是指程序员)就不用考虑数据包的细节问题,只要当作信息流就可以了。被我们称为“互联网”的大部分服务都使用TCP。

互联网协议(IP)提供的是不可靠、无连接的数据包传递服务。所谓“无连接”,就是每个IP包都是自包含的,和其他IP包无关。IP协议没有状态或记忆,也就是说这个协议一旦把包传给下一个网关,就不再保存关于这个包的任何信息

至于“不可靠的”,可能和字面上看起来的含义略有不同。IP协议采取“尽力而为”的做法,并不能保证数据包传送的质量,某些时候的出错会造成很大的麻烦。包可能丢失或者损坏,接收到的顺序可能和发送的顺序不一致,也许送达得太快而无法处理,也许送达得太慢而失去作用。当然,实际使用的时候,互联网协议是相当可靠的,但是当包中途丢失或损坏的时候,该协议确实不会尝试修复

IP包最大约为65KB。这样长消息就要拆分成小数据块分别发送,到了远端再组装起来

IP包中有个很有趣的部分是生存时间(Time To Live, TTL)。TTL是个单字节字段,由包的发送方设置一个初始值(通常是40左右),每经过一跳处理该数据包的网关就减1,当减到0的时候,就丢弃这个包,并给发送者返回一个报错包

互联网中一次典型的传递数据包的过程通常会经过15到20个网关,所以经过了255跳的包显然有问题,很可能是走环路了。TTL并不能消除环路,但能防止单个的包在遇到环路时一直绕圈

在互联网协议簇中,高层协议基于IP层的不可靠服务合成可靠的通信,其中最重要的就是传输控制协议——TCP。TCP能为用户提供可靠的双向数据流:一端输入数据,另一端流出数据,延迟很小,出错率很低,仿佛是一条从一头到另一头的直连线缆。

TCP报文段不仅包含实际数据,还有控制信息构成的头部,其中包括方便接收方知晓收到的包代表数据流中哪部分的序号。通过这种方法,就可以发现丢失的报文段并重传之。TCP报文段的头部还包括错误检测信息。这样,如果报文段出错,就很容易检测出来。每个TCP报文段都放在一个IP包里传输

接收方必须对收到的每个报文段返回确认或者否认的应答。我给你发的每一个报文段,你都要返回一个应答以表明你收到了。如果在适当间隔之后我还没收到应答,那我就认为这个报文段已丢失,然后重新发送。同样,如果你预期会收到某个特定的报文段却没收到,那就得给我发送否认应答(比如“未收到27号报文段”),这样我就会知道需要重新发送

如果应答报文本身丢失了,情况就会更复杂。TCP使用若干计时器来检测此类错误,如果计时器超时,就认为出错。如果某个操作耗时过长,就会尝试启动恢复程序。最终,某个连接可能会因为“超时”而被终止。你也许见过失去响应的网站,那就是遇到了这种情况。这些都是TCP协议的一部分。

TCP协议同样还包含高效处理这个问题的机制。比如,发送方可以在未收到上个包的应答信息时就继续发送下个包,接收方也可以为接收到的一组包回送一个应答。在通信顺畅的时候,这样做可以降低应答带来的开销。而当网络发生拥塞、开始出现丢包现象时,发送方就迅速回退到较低的传输速率,并且只缓慢地重新发送

在两台计算机主机之间建立TCP连接时,不仅要指定计算机,还要指定计算机上的端口。每个端口表示一个独立的会话。端口用两字节(即16位)二进制数表示,于是就有65536个可用端口。这样,在理论上每台主机可以同时承载65536个TCP会话

大约有一百个众所周知的端口预留给了标准服务。比如,Web服务器使用80端口,邮件服务器使用25端口

TCP协议实现细节远比这复杂得多,但基本原理就是这些。TCP和IP最初由文特·瑟夫(Vint Cerf)和鲍勃·卡恩(Bob Kahn)于1973年设计,他们因此一起获得了2004年图灵奖。尽管经历了多次改进,但在网络规模和通信速度已经增长了多个数量级的情况下,TCP/IP协议还是能基本保持不变,这充分证明最初的设计是相当棒的

你可以使用Telnet访问Amazon。Telnet是一种用于在另一台机器上建立远程登录会话的TCP服务,通常使用23端口,但也可以指定其他端口。

GET是HTTP请求的若干方法之一,“/”的意思是请求服务器上的默认文件,HTTP/1.0则是协议名称和版本号

Telnet提供了一种访问远程计算机的方式,就像直接连接到远程计算机一样

Telnet并不提供安全措施。如果远程系统能接受没有口令的登录,那就不需要口令。如果远程系统向客户端要口令,Telnet会以明文形式将客户端的口令发送过去。因此,任何监视数据流的人都能看到口令

现在,除了不讲究安全的场合,Telnet已经很少用了,原因之一就是它毫无安全性可言。而Telnet的继任者SSH(Secure Shell)则因为双向加密了全部通信而得到广泛使用,可以用来安全地交换信息。SSH使用22端口

SMTP做了足够多的安全限制,因而即使把你自己的计算机当成邮件服务器进行本地操作,也会遇到麻烦

SMTP本身也可以复制邮件内容,邮件系统会跟踪内容和头部的传送。因此,如果不想让发送的邮件内容被别人看到,一定要从发送方就要进行加密。但有一点要记住,加密邮件内容并不会隐藏发件人和收件人的身份。流量分析能揭示是谁在与谁通信;这些元数据通常可以提供与实际内容一样重要的信息

SMTP只是将邮件从源主机传送到目的邮件服务器,然后就不管用户如何访问邮件了。一旦邮件到达目的邮件服务器,它就原地等待直到收件人取走

通常收邮件由互联网邮件访问协议(Internet Message Access Protocol, IMAP)来处理。根据IMAP协议,你的邮件保存在服务器上,你可以从好几个地方访问它。IMAP能确保即使邮箱有多个阅读者和访问者在更新使用(同时从浏览器和手机处理邮件),你的邮箱始终处于一致状态。不需要对消息进行多重复制,也不需要在计算机之间复制它们

Napster向我们展示了另外一种模型。它提供了一个中心目录,列出了现在可共享的音乐,但音乐文件本身还是存储在我们自己的计算机上;当文件传输时,文件直接从一个Naspter用户传到另一个用户,并不经过中心系统。这样的组织方式就叫点对点,共享者就是其中的对等点(peer)。

目前大多数文件共享,无论合法与否,都使用一种叫作BitTorrent的点对点协议,它是由Bram Cohen在2001年开发的。BitTorrent对于分享电影和电视节目等较大的热门文件特别有效,因为每个开始使用BitTorrent下载文件的网站,也必须开始上传文件的片段给其他想要下载的网站。文件可以通过搜索分布式目录找到,用一个小的“torrent文件”来识别一个追踪器,这个追踪器维护着谁发送和接收了哪些块的记录。BitTorrent用户很容易被检测到,因为协议要求下载者也上传,因此他们很容易在受版权保护的材料的行为中被发现

DMCA有时被用于一种反竞争的方式,这可能不属于最初意图。例如,飞利浦公司生产的“智能”联网灯泡允许控制器调节其亮度和颜色。2015年底,飞利浦宣布将对固件进行修改,使飞利浦的灯泡只能与飞利浦的控制器一起使用。根据DMCA,将阻止任何人对软件进行逆向工程以使用第三方的灯泡。这引发了相当大的抗议,飞利浦在这个具体的案例中做出了让步,但其他公司继续使用DMCA来限制竞争,例如更换打印机或咖啡机的墨盒。

从许多方面来看,物联网是一个好主意,而且可以肯定的是未来会有越来越多的创新出现。但这里也存在一个很大的缺点,这些专用设备比通用设备更容易出现问题。黑客攻击、非法入侵、破坏等等都很可能发生,而且事实上发生的机率更大,因为我们对物联网安全和隐私的关注远远落后于个人电脑和手机的技术水平。数量惊人的设备正在“打电话回家”,即将信息发送回制造它们的国家的服务器

这样的例子太多了,我们随便举一个吧。2016年1月,某网站允许其用户搜索网络摄像头的视频内容,这些摄像头没有任何保护的措施。该网站提供了“大麻种植园、银行后房、儿童、厨房、客厅、车库、前花园、后花园、滑雪场、游泳池、大学和学校、实验室和零售店的收银机摄像头”的图片。人们可以想象到从简单的偷窥到更糟糕的用法

一些儿童玩具可以连上互联网,这就带来了另一种潜在的危害。一项研究表明,一些玩具包含了分析代码,可以用来跟踪儿童,以及包含一些不安全机制用于允许将玩具作为攻击的载体

可能制造商觉得处理安全问题太费钱了,或者对消费者来说设置太复杂了,或者可能只是程序执行得不好

在2019年底,一名黑客发布了50万份物联网设备的IP地址和Telnet密码,他是通过扫描端口22上有响应的设备,然后尝试“admin”和“guest”等默认账户和密码,从而发现这些有漏洞的设备的

电力、通信、交通和许多其他的基础设施系统已经连接到互联网,但没有对保护它们给予足够的重视

2015年12月有报道称,某家制造商的风力涡轮机有一个支持网络的管理界面,可以轻易地攻击(只需编辑URL)网络界面,就可以关闭它们正在产生的电力

互联网是基于数据包的网络。在互联网中,信息被封装在一个个标准格式的数据包里发出,动态地在一个巨大的不断变化的网络集合里被路由。

互联网给当前在线的每台主机分配唯一的IP地址,同一个网络内的主机共享同一个IP地址前缀

在传统电话网络中,所有的智能都在网络中,而终端,比如老式电话,都是真正的哑巴,基本只负责连接网络和传递语音

哑网络”模式一直具有很高的生产力,因为它意味着任何有好想法的人都可以创建智能端点,并依赖网络来承载字节,期待电话或有线电视公司实施或支持好的想法是行不通的

运营商希望从电话服务中赚钱,但基本上现在只能从传输数据中获得收入了

最后,注意早期的协议和程序是如何信任他们的用户的。Telnet以明文方式发送密码。在很长一段时间里,SMTP将邮件从任何人转发给任何人,而不以任何方式限制发件人或收件人。这种“开放中继”服务对于垃圾邮件发送者来说非常棒——如果不需要直接回复,就可以谎报源地址,这使得欺诈和拒绝服务攻击变得容易。

攻击有线的以太网和光缆需要找到其中的线缆并进行物理连接,而对无线网络的攻击不需要物理访问来进行窥探,只需接近即可

“WorldWideWeb(W3)是一种广域超媒体信息检索原始规约,目的是访问巨量的文档。”——摘自第一个网页,参见info.cern.ch/hypertext/WWW/TheProject.html,1990年

互联网最常见到的面孔就是万维网(World Wide Web),或者现在常简称为Web。现在有一种将互联网和万维网混为一谈的趋势,但两者其实并不相同

互联网是一种通信基础设施或基板,它可以让全世界数百万台计算机轻松地彼此交换信息

万维网连接着提供信息和请求信息的计算机,提供信息的叫服务器,请求信息的叫客户端。万维网使用互联网建立连接和承载信息,并为互联网支持的其他服务提供接口。

像许多伟大的理念一样,万维网在本质上是简单的。考虑到构建一个无处不在的、高效的、开放的和基本免费的(这是一个很大的附带条件)基础网络,只有四件事是必不可少的

首先是URL(Uniform Resource Locator,统一资源定位符),用于指定要访问信息源的名称,比如http://www.amazon.com。

其次是HTTP(HyperText Transfer Protocol,超文本传输协议),上一章作为高层协议的示例刚刚介绍过。HTTP客户端请求某个特定的URL,而服务器返回客户端请求的信息。

第三个是HTML(HyperText Markup Language,超文本标记语言),描述服务器返回信息的格式或表现形式。HTML同样很简单,你只需知道很少的知识就能使用它。

最后是浏览器,即运行在你计算机上的Chrome、Firefox、Safari或者Edge等程序,它使用URL和HTTP向服务器发送请求,然后读取并显示服务器返回的HTML。

万维网的诞生于1989年。当时,在日内瓦附近的欧洲物理研究中心(CERN)工作的英国计算机科学家蒂姆·伯纳斯-李(Tim Berners-Lee),为便于通过互联网共享科学文献和研究结果而创建了一个系统。他的设计包括URL、HTTP和HTML,以及一个只能用文本模式查看可用资源的客户端。在CERN的网站line-mode.cern.ch/www/hypertext/www/TheProject.html上有一个最初版本的模拟。

这套系统在1990年投入使用。我在1992年10月访问康奈尔期间还亲眼见到有人在使用它。说来惭愧,我当时并没觉得它令人印象深刻,也根本没想到6个月后诞生的第一个图形界面浏览器会改变世界

世界上第一个图形界面的浏览器Mosaic是由伊利诺伊大学的一群学生开发的。Mosaic的首个版本发布于1993年2月,很快就大获成功。仅仅一年之后,第一个商业浏览器Netscape Navigator面世。Netscape Navigator是早期的成功者,而那时微软对互联网的蓬勃发展毫无意识。但这个软件巨头还是觉醒了,随后很快推出竞争产品Internet Explorer(IE)。IE后来居上,成为最常用的浏览器,占据了很大的市场份额。

微软被控利用其在操作系统领域的统治地位,将竞争对手Netscape排挤出了浏览器市场。微软输了官司,被迫改变了一些商业行为。

如今,Chrome是笔记本电脑、台式机和手机上广泛使用的浏览器。2015年,微软发布了新的Windows 10浏览器Edge,以取代IE。Edge最初使用的是微软自己的代码,但自2019年以来,它已经改用谷歌的开源Chromium浏览器。Edge的市场份额低于Firefox,IE则更低

Web技术的发展,由万维网联盟(World Wide Web Consortium,W3C,其网站为w3.org)这个非营利机构管理,或者至少是指导。W3C的创始人和现任主席伯纳斯-李没想过靠自己的发明赚钱,而是慷慨地提出让所有人免费使用万维网,反倒是很多投身互联网和万维网的人都借助他的工作变得非常富有。2004年,英国女王伊丽莎白二世授予伯纳斯-李爵士勋章。

这样相互链接的页面就叫超文本(意思是“不仅仅是文本”)。超文本实际上是个老概念,但浏览器把它带到每个人面前。

URL自身编码了信息。第一部分“http”是协议名,可能采用好几种协议。最常见的是HTTP,你也将看到其他协议,包括“file”表示信息来自本机(而不是Web上),“https”表示采用经过加密的HTTP协议的安全版本

接下来,“://”后面是域名,即服务器的名字。域名后面可以跟着斜线(/)和一串字符。该字符串会原样传递给服务器,由服务器决定如何处置。最简单的情况是域名后什么都没有,连斜线也没有。在这种情况下,服务器将返回默认页面,比如index.html。

文件名之后如果有问号,一般表示问号前面的部分是程序,意味着希望服务器运行该程序,并将问号后面部分作为参数传给该程序

默认情况下,图像文件来自和原始文件相同的网络位置,但它可以来自网络上的任何地方

在HTML中,有些标签是自包含的,比如;有些则有起始标签和结束标签,比如和;还有些标签,如

,虽然严格定义里要求有闭合标签

,但在实际中并不需要。我们的示例使用了结束标签。缩进和换行并非必需,但加上了会让HTML文本更易读。

HTML和CSS都是语言,但不是编程语言。它们有正式的语法和语义,但它们没有循环和条件,所以你不能用它们来表达算法。

还有一种从客户端(你的浏览器)向服务器传递信息的机制,叫作通用网关接口(Common Gateway Interface, CGI),这个命名并不直观,很难看出它实际是用来传递用户名和密码,或搜索查询,或通过单选按钮和下拉菜单选项等信息

该机制由HTML的

标签提供。通过标签,你可以放入常见的用户界面元素,比如文本输入区、按钮,复选框等。如果再加上一个“提交”按钮,按下去就会把表单里的数据发送到服务器并发出请求,让服务器用这些数据作为输入,来运行指定的程序

HTTP协议是无状态的。“无状态”的意思是,HTTP服务器不必记住不同客户端发送的请求,只要向客户端返回了请求的页面,它就可丢弃有关每次信息交换的全部记录

1994年,Netscape公司发明了一种叫cookie的解决方案。cookie是在程序之间传递的一小段信息,这个名字虽有卖萌之嫌,但已经为广大程序员接受。服务器向浏览器发送页面时,可以附加若干个浏览器可存储的文本块,每个文本块就是一个cookie,最大为4000字节左右。当浏览器再次访问同一个服务器时,再把cookie发送回服务器。实际上,服务器就是这样利用客户端的记忆来记住之前客户机上一次访问的一些情况

服务器通常为每个客户端分配一个唯一的识别码,并将其包含在cookie里;而和这个识别码相关联的永久信息如登录状态、购物车内容、用户喜好等,则维护在服务器上的数据库中。每当用户再次访问这个网站时,服务器就用cookie识别出用户原来之前来过,从而为其建立或恢复信息

每个cookie都有名字,一台服务器可以为一次访问存储多个cookie。cookie不是程序,也不包含动态内容。它是完全被动的,cookie只是一串存储在浏览器中供以后返回服务器的字符,只有来自服务器的内容才能返回到该服务器。浏览器只能将cookie发送给它当初来自的服务器。cookie有失效时间,过期了就会被浏览器删除。但是否接受或返回cookie并没有强制规定

但总会有些有违初衷的坏事发生,cookie也被用到了人们不太喜欢的用途上。最常见的就是在用户浏览时跟踪其行为,生成用户访问网站的记录,然后向用户投放定向广告

万维网最初被设计的时候并没有特别利用客户端是一台功能强大的计算机、一个通用可编程设备这一事实

最初的浏览器可以代表用户生成对服务器的请求、发送表单里的信息,并能在辅助程序的帮助下显示图片、声音等需要特殊处理的内容。不久之后,浏览器就能运行从网上下载的代码了,有时又称之为动态内容。正如预期的那样,动态内容会产生显著的后果,有些是积极的,有些则绝对不是

由于各种原因,Java最终没有成为扩展浏览器的主要途径。Java本身应用广泛,但与浏览器集成时却受限较多,现在Java已经很少用于扩展浏览器了

1995年,Netscape还推出了一种专用于其浏览器的新语言JavaScript。选择这个名字只是出于市场宣传的目的,其实JavaScript跟Java没有任何关系,唯一相关的是两者写出来的程序都看上去像C语言

Java和JavaScript的实现都使用了虚拟机,但两者的技术差别很明显。Java源代码在其创建之处编译,生成的目标代码发送到浏览器解释运行,因此你看不到最初的Java源代码是什么样子的;与之相反,JavaScript发送到浏览器的就是源代码,就在浏览器里编译。接收者可以看到正在执行的源代码,并可以进行研究、修改以及运行

借助浏览器自身的代码或Apple QuickTime和Adobe Flash这样的插件,浏览器也可以处理其他语言和内容

插件是按照需求动态加载到浏览器的程序,一般由第三方编写。如果你访问的页面里有浏览器不能处理的内容,浏览器会提示你“获取插件”。意思就是要你下载一个新程序,在你的计算机里紧密配合浏览器一起运行

插件能做什么呢?理论上它可以为所欲为。所以你不得不信任插件的发行者,否则就没法显示那些内容。插件是编译好的代码,通过调用浏览器提供的API,作为浏览器的一部分运行

常用的插件包括广泛应用的Flash视频和动画播放器,以及用于PDF文件的Adobe Reader。关于插件,如果我们长话短说,那就是:如果你信任插件的来源,那在使用它的时候,就跟使用其他有bug和会监控你行为的代码没什么两样。然而,长期以来Flash一直存在重大的安全漏洞,现在已不再使用

HTML5为浏览器带来了新功能,可以减少对插件的依赖,尤其是在视频和图形方面。但在很长一段时间内,插件还会继续存在

在微软一篇名为《10条永恒的安全法则》的文章中,第一条法则是:“如果一个坏人能说服你在你的电脑上运行他的程序,那它就不再是你的电脑了。”在允许JavaScript和插件方面要谨慎保守

我曾经收到一封邮件,声称里头有俄罗斯网球运动员安娜·库尔尼科娃的照片,鼓励我打开看看。该文件的名字是kournikova.jpg.vbs,但是扩展名.vbs隐藏了起来(Windows错误设计的功能),收件人很难发现它并非照片而是Visual Basic程序。幸好我用的是UNIX系统下的老古董邮件程序,只支持文本模式,不支持点击运行。于是我把“照片”另存为文件待稍后检查,这个程序也就暴露了。

病毒的传播需要人工介入,也就是只有你的操作才能催生它的传播;而蠕虫的传播却不需要你的协助

随着1991年微软在Office套件程序尤其是Word中包含Visual Basic,病毒的传播变得更加容易了。由于大多数版本的Word中包含VB解释器,Word文档(.doc文件)、Excel和PowerPoint文档中可以包含VB程序。因此写个VB程序在文档打开时获取控制易如反掌。而且,由于VB能访问Windows操作系统的全部功能,这类程序可以为所欲为

通常事情发生的次序是,如果本机还没有的话,病毒会首先安装在本机上,然后想方设法将自身传播到别的系统上。

这样的VB病毒在20世纪90年代中后期很猖狂。由于Word的默认策略是不经用户允许就盲目地运行VB程序,这类病毒传播得非常快,致使很多大公司必须关掉所有计算机、逐台查杀才能消灭病毒。VB病毒尽管现在还有,但只需改变Word和其他同类程序的默认行为就能严重削弱它们的破坏力。另外,现在的很多邮件系统收到新邮件时,在将邮件送达收件人之前,会先剔除其中的VB程序和其他可执行内容

VB病毒很容易编写,甚至一些没有多少经验的编程者也可以制造,以致写这些东西的人被称为“脚本小子”

2010年底,一个叫“震网”(Stuxnet,也称为“超级工厂”)的复杂蠕虫出现在过程控制计算机中,它的主要攻击目标是伊朗的铀浓缩设备

“震网”用了一个很微妙的攻击方法,即控制离心机的转速波动,看上去只会引起正常磨损,却会导致离心机损坏甚至报废;同时,对监控系统报告说运行正常,于是没人注意到离心机出现问题。至今,仍然没有人站出来承认对此负责,尽管人们普遍认为以色列和美国参与了其中。

如果硬盘中包含的文件具有一个引人入胜名字,如“ExecutiveSalaries.xls”,则可能根本不需要自动运行的功能了

垃圾邮件过滤是机器学习(machine learning)的一个主要应用。给定一组被标记为垃圾邮件或非垃圾邮件的示例训练集,机器学习算法根据它们与训练集特征的相似性对后续输入进行分类

要想从源头制止垃圾邮件非常困难,因为垃圾邮件的源头通常都极其隐蔽

要编造一封看起来正规的邮件是很容易的,版式和图片都可以从真实的网站上复制。使用无效的回信地址也很容易,因为邮件系统发件时并不检查发件人的真实性。和垃圾邮件一样,钓鱼也几乎不花任何成本,即使是很小的成功率也能带来利润

2020年7月,Twitter遭受了一次尴尬的攻击。比尔·盖茨、杰夫·贝索斯、埃隆·马斯克、巴拉克·奥巴马和乔·拜登等多位知名人士的账户被用来发推文,内容是“用比特币给我们寄1000美元,我们会给你寄回2000美元。”很难相信真有人会被骗到,更不用说有能力发送比特币的人了。但显然在Twitter关闭它之前,已有数百人这么做了

此外,入侵者往往会在个人计算机上安装僵尸(zombie)程序。这类程序平时潜伏着,一旦控制者从互联网发来指令,就会被唤醒并执行诸如发送垃圾邮件等恶意行为。这类程序通常被称为僵尸程序(bot),由它们组成的具有共同控制的网络被称为僵尸网络(botnet)。在任何时候,都有数千个已知的僵尸网络和数百万个僵尸程序可以被调用服务。向潜在攻击者出售僵尸程序是一项生意兴隆的业务。

我把计算机用户的防御手段分成三类:第一类非常重要,第二类中等重要,第三类则要看你的多疑程度。(如你所知,我非常多疑,大多数人大概不会那么多疑。)

◆ 第四部分 数据

“如果一台计算机能思考、学习和创造,那它将依靠赋予它这些能力的程序。……它将是这样一个程序,能通过某种方式分析其自身的性能,诊断其故障,并做出改变,以增强其未来功能的有效性。”——赫伯特·西蒙,《管理决策新科学》,1960年

“我的同行们都在研究人工智能,而我呢,却在研究人们与生俱来的愚蠢。”——现代行为经济学创始人阿莫斯·特沃斯基(1937—1996),摘自2019年4月的《自然》杂志

如果我们把稳定增长的计算能力和内存应用于大量数据,并引入一些复杂的数学,就有可能解决人工智能领域许多长期存在的问题:让计算机按照我们通常认为只属于人类独有的方式运行。

图像识别系统可以非常有效地分离出图像的组成部分,然后给出这些组成部分是什么,尽管它们经常被愚弄

人工智能(artificial intelligence)包含的范畴较为广泛,指使用计算机来做我们通常认为只有人类才能做的事情的:“智能”是我们人类赋予自己的荣誉,而“人工”意味着计算机也在做这件事

机器学习(machine learning)是人工智能的一个子领域,该技术被用于训练算法以便它们能够做出自己的决定,从而执行一些我们称之为AI的任务

深度学习是机器学习的一种特殊形式,它使用的计算模型,至少从比喻意义上说,类似于我们自己大脑中的神经网络

深度学习的实现松散地模仿人类大脑的处理方式:一组神经元用于检测低级特征;它们的输出为其他神经元提供输入,这些神经元根据较低水平的特征识别较高水平的特征,以此类推。随着该系统的不断学习,一些神经元之间的联系被加强,而另一些被削弱。

深度学习是一种高效的方法,尤其是对计算机视觉。它是机器学习研究最活跃的领域之一,有大量不同的模型

专家系统确实在一些领域取得了一些成功,包括客户支持、机械系统的维护和维修,以及其他关注的领域,但最终人们发现,专家系统也有一些大的局限性。在实践中,很难收集到一套全面的规则,而且有太多的例外。

机器学习的基本思想是给一个算法输入大量的示例,从而让它自己“学习”,不需要给定规则,也不需要明确编程来解决特定的问题

机器学习算法试图提高获得良好结果的概率,但不保证结果能完美无缺

训练结束后,算法根据从训练集中学到的知识对新的条目进行分类或预测它们的值

基于标记数据的学习称为监督学习(supervised learning)

手写数字识别是一个众所周知的技术。NIST,即美国国家标准与技术研究所,提供了一个包含60000张训练图像和10000张测试图像的公共测

试套件。图12.1显示了一个小示例。机器学习系统在这些数据上表现很好:在公开比赛中的错误率低于0.25%,也就是说,大约400个字符中会有一处错误。[插图]

机器学习算法在很多方面都可能失败——例如,“过拟合”,即算法在训练数据上表现良好,但在新数据上表现很差

与监督学习相比,无监督学习使用的是未标记的训练数据,即没有标记或做了任意标记的数据

有一种流行的算法——k-means聚类,该算法尽最大努力将数据分配给k组,其方式是最大化同一组中各个条目的相似性,同时最小化一组与另一组之间的相似性

如果计算机能模拟人脑的工作方式,它们就能在智力任务上表现得和人类一样好。这是人工智能的圣杯,人们已经尝试这种方法很多年了

大脑的功能是基于神经元的连接,神经元是一种特殊的细胞,对触摸、声音、光线或其他神经元的输入等刺激做出反应。当它的输入刺激足够强时,一个神经元就会“激发”并在其输出端上发出信号,这反过来可能会引起其他神经元的反应。(这当然过于简单化了。)

这种网络的核心思想是,前面的层识别低级特征,例如识别可能是图像边缘的像素模式。后面的层识别更高级的特征,比如物体或颜色区域,最后的层识别像猫或脸这样的实体

深度学习中的“深度”一词指的是神经元有多层;根据特定的算法,可能只有几层,也可能是十几层或更多

网络通过反复处理输入和生成输出进行学习,进行大量的迭代。在每次迭代中,算法测量神经网络所做的与我们希望它做到的之间的误差,并修改权值,以尽量减少下一次迭代的误差。当训练时间耗尽,或者权重变化不大时,它就会结束计算

作为学习过程的一部分,他们会自己发现特征,不管这些特征是什么。这导致了神经网络的一个潜在缺点:它们不能解释它们所识别的“特征”,因此不能提供对其结果的具体解释或理解。这就是为什么我们必须小心谨慎,不要盲目依赖它们的原因之一。

深度学习在与计算机视觉相关的任务中尤其成功,即让计算机识别图像中的物体,有时还能识别特定的实例,比如人脸

计算机视觉是许多机器人应用的核心,特别是自动驾驶汽车,显然必须能够解读周围的世界,而且必须以很快的速度完成这一任务

深度学习最引人注目的成功之一,是创造出了能出色玩国际象棋和围棋等最难的人类游戏的算法,而且比最优秀的人类棋手更出色

如果你想自己做一些机器学习的实验,谷歌的网站teachablemachine.withgoogle.com可以让你轻松地进行图像和声音识别等任务的实验。

自然语言处理(NLP)是机器学习的一个子领域,研究如何让计算机处理人类语言——如何理解一些文本的意思,总结它,把它翻译成另一种语言,或把它转换成语音(或把语音转换成文本),甚至生成有意义的文本,看起来和人类完成的一样好

情感分析(sentiment analysis)是自然语言理解的一种有趣的特殊情况,它试图确定一段文本从本质上是积极的还是消极的

人工智能面临的另一个挑战是以人类水平进行对话。这又回到了图灵的计算机智能测试,我们在第3章的末尾描述过。智能的对话需要理解对方所说的内容,并能够生成合适的回应

人工智能和机器学习为我们在计算机视觉、语音识别和生成、自然语言处理、机器人等许多领域带来了突破。与此同时,它们引起了人们对技术的公平性、偏见、责任和技术使用的道德程度的严重担忧。也许最重要的问题是,机器学习系统的答案可能“看起来是正确的”,但却只是因为它们反映了使用的数据中带有的偏差

人工准备的训练数据很有可能把算法引入歧途

正是由于英国人靠阿兰·图灵的计算技术和专业知识破解了恩尼格玛密码机(见图13.1),解密了德军的军事情报,才使得第二次世界大战结束时间大大提前。

密码学的基本思想是,假设Alice和Bob想要交换消息,并且即使对手可以读取交换的消息,也需要保持其内容的私密性

密码系统必须假定攻击者知道并完全理解密码系统的工作原理,从而将所有的安全性都寄托在密钥上。与此相反的做法是假定对手不知道系统用什么加密方案、如何破解,这被称为隐匿式安全(security by obscurity),即使可以工作,也不会长久

如果有人鼓吹他们的加密系统十分安全,却不愿说出其工作原理,那就可以确信它并不安全

加密系统的开放式开发至关重要。密码系统需要尽可能多的专家的经验,以探查漏洞

目前使用的加密系统基本可分成两类。一类是历史较长的密钥加密(secret-key cryptography),也称对称密钥加密,因为加密和解密要使用相同的钥匙。“对称”这个词能更好地描述其本质,但“密钥”则让人更容易区分它与另一类更新的加密系统:公钥加密(public-key cryptography)

直到21世纪初,最常用的密钥加密算法是DES(Data Encryption Standard,数据加密标准),该算法由IBM和NSA(National Security Agency,美国国家安全局)于20世纪70年代初共同开发。尽管有人怀疑NSA在DES里安排了一个秘密的后门机制,这样他们就能轻松破解用DES加密的信息,但这种怀疑从未得到证实。在任何情况下,DES总是使用56位密钥,随着计算机的运算速度越来越快,56位的长度被证明太短了。到1999年的时候,一台相当便宜的专用计算机,就可以用一天时间使用蛮力攻击破解DES密钥。这导致了具有更长的密钥的新算法的产生。

这些算法中使用最多的是AES(Advanced Encryption Standard,高级加密标准)。该算法是作为全球公开竞赛的一部分开发的,竞赛的赞助方为美国国家标准技术研究所(National Institute of Standards and Technology, NIST)。当时,来自全世界的参赛选手提交了几十个算法。经过激烈的公开评测。比利时密码学家琼·德门(Joan Daemen)和文森特·赖伊曼(Vincent Rijmen)发明的Rijndael算法摘取桂冠,并于2002年成为美国政府的官方标准。这个算法已归入公有领域,任何人都可以无偿使用,也无需许可证。AES支持128、192和256位三种密钥长度,潜在的密钥数量非常多,用蛮力攻击就算很多年也不会有结果,除非能发现该算法的某个弱点。

我们可以量化地描述这个问题。如果像GPU这样的专门处理器每秒可以执行1013次运算,那么一百万个GPU每秒可以执行1019次运算,也就是每年大约3×1026次,也就是大约290次。这距离2128还差很远,所以即使是AES-128也应该不会受到蛮力攻击的破解

AES和其他密钥加密系统面临的一个大问题是密钥分发(key distribution):参与秘密通信的每一方都必须知道所用的密钥,所以必须有绝对安全的渠道把密钥送到通信参与方

还有一个问题是密钥扩散(key proliferation):要想和不相关的各方进行独立的秘密对话,就要为每组会话准备不同的密钥。这就导致密钥分发更加困难

公钥加密采用了与密钥加密完全不同的思想,是怀特菲尔德·迪菲(Whitfield Diffie)和马丁·赫尔曼(Martin Hellman)于1976年在斯坦福发明的,借鉴了拉尔夫·默克尔(Ralph Merkle)的一些思想。迪菲和赫尔曼因为这项工作共同获得了2015年的图灵奖。这个想法由詹姆斯·埃利斯(James Ellis)和克利福德·科克斯(Clifford Cocks)在更早的几年前独立发现,他们是英国政府通信总部的密码学家,但他们的工作被一直保密到1997年,所以他们不能发表该成果,因此也与大部分荣誉失之交臂

在公钥加密系统里,每个人都有一个密钥对(key pair),包含一个公钥和一个私钥。这对密钥是在数学上有关联的整数,具有如下性质:用其中一个密钥加密过的消息只能用另一个密钥解密,反之亦然。在公钥加密系统中,只要密钥足够长,攻击者不论是想解密私密消息,还是想从公钥推算出密钥,从计算的角度而言都是不可行的。攻击者可以采用的哪怕是最著名的算法,所需要的运行时间也随密钥长度呈指数增长

在实际使用中,公钥是真正很公开的:任何人都能拿到,一般是公布在网站上;私钥则一定要严格保持私密,是一个只有这个密钥对的主人才知道的秘密

公钥加密的一个缺点是其算法的运算速度慢,比AES这种密钥加密算法要慢好几个数量级。所以,通常不会用公钥加密算法加密全部数据,而是分两步走:先用公钥加密协商出一个临时的密钥,再使用AES传输大量的数据。

上述通信过程的每个阶段都是安全的,首先是用公钥加密算法设置临时密钥,然后再用AES交换大量数据

公钥加密方案也并非完美无缺。如果Alice的私钥泄露了,之前别人发给她的所有消息就形同明文,而她之前的签名也就不可信了。另外,尽管大多数密钥生成方案都包括密钥生成时间和失效时间的信息,但撤销一个密钥,即宣布某个密钥从此以后失效,是很困难的。一种称为前向保密(forward secrecy)的技术可以解决这个问题。每条消息都使用如上所示的一次性密码进行加密,然后该密码被丢弃。如果一次性密码的生成方式使对手无法重新创建它们,那么即使私钥被泄露,知道一条消息的密码也无助于解密以前或将来的消息。

最被广泛使用的公钥加密算法称为RSA,这个算法是麻省理工学院的三位计算机科学家罗纳德·李维斯特(Ronald Rivest)、阿迪·沙米尔(Adi Shamir)和伦纳德·阿德曼(Leonard Adleman)在1978年发明的,RSA就是这三位发明人姓氏的首字母缩写。RSA算法的可靠性来自于对大的合数做因式分解的难度。RSA的工作原理是生成一个很大的整数,至少500位长度的十进制数字,这个整数有两个素数因子,每个长度约为二者乘积的一半,以此作为生成公钥和私钥的基础。知道这两个素数因子的人(即私钥的持有者)能迅速解开密文,而其他人要解密就得先分解素因子。由于计算量过大,这实际上是不可能完成的。李维斯特、沙米尔和阿德曼因为发明RSA算法而获得了2002年图灵奖

密钥的长度很重要。据我们目前所知,要把一个大整数分解成两个长度大致相同的素数的乘积,所需的计算工作量随着其长度的增长而迅速增长,这样的因式分解基本是不可能的。RSA实验室是持有RSA专利的公司,曾于1991年到2007年举办过分解素因子大赛。它公布了一个包含着长度越来越长的巨大合数的列表,设立奖金给第一个能对每个合数进行因数分解的人。最小的合数有100多位,很快就被分解了。2007年这个竞赛停办之时,分解出来的最大合数有193位(二进制640位),奖金是2万美元。2019年,RSA-240(240位,795位)被分解。这个列表还挂在网上,感兴趣的读者不妨一试。

因为公钥算法很慢,所以一般不直接用公钥算法对文档签名,而是签署从原始文档提取出来的小得多的文档。选用适当方法生成的这种小文档无法被伪造。这样的小文档就叫作消息摘要(message digest)或密码散列(cryptographic hash),其创建方法是用某种算法把任意输入的比特流加密成固定长度的比特流,也就是摘要或散列,最终得到的结果具有如下特性:无法通过计算找到别的输入来生成同样的摘要

此外,输入中最轻微的改变都会让输出摘要中大约一半的比特发生变化。这样,通过比较接收者计算生成的文档摘要与原始摘要是否匹配,就能有效检测文档是否遭到了篡改。

有几种消息摘要算法被广泛使用:一个是以上展示过的罗纳德·李维斯特开发的MD5,它生成128位的摘要。另一个是SHA-1,来自NIST(美国国家标准技术研究所),生成160位的摘要。已有研究表明MD5和SHA-1都有弱点,因此不赞成使用它们。SHA-2是美国国家安全局开发的一系列算法,目前还没有已知的弱点。尽管如此,NIST开展了一个公开的竞赛,类似于产生AES的竞赛,以创建一个新的消息摘要算法。获胜者在2015年选出,现在被称为SHA-3。SHA-2和SHA-3支持从224位到512位不等的摘要大小。

典型的浏览器知道数量惊人的证书颁发机构——在我的Firefox版本中有将近80个,而在Chrome版本中有超过200个,其中大多数都是我从未听说过的机构,并且位于遥远的地方

Let’s Encrypt(让我们加密)是一个非营利性的证书权威机构,它向任何人提供免费的证书,其理念是,如果获得证书很容易,最终所有网站都将使用HTTPS,所有流量都将被加密。到2020年初,Let’s Encrypt已经发行了10亿张证书

Tor浏览器是使用Tor的最常见方式,Firefox的一个版本已经被配置为使用Tor进行传输,Tor还能适当地进行Firefox的隐私设置。从网址torproject.org下载并安装它,然后像其他浏览器一样使用它,但要注意关于如何安全地使用它的警告

如果你感觉对于稳私特别偏执,可以尝试一种名为TAILS的系统,即“失忆隐身实时系统”,它是在Linux系统上运行的,能安装在DVD、USB驱动器或SD卡等可引导设备上。它在启动后能运行Tor技术和Tor浏览器,并且不会在运行它的电脑上留下任何痕迹。软件在TAILS的运行下使用Tor连接到因特网,所以你应该是匿名的。它也不在本地二级存储上存储任何东西,而只是使用主内存;当计算机在TAILS会话之后关闭的时候,内存中的内容将被清除。这使你无需在主机上留下任何记录即可处理文档。TAILS还提供了一套其他加密工具,包括OpenPGP,它允许对邮件、文件和其他任何内容进行加密。TAILS是开源软件,可以从网上下载

事实证明,巧妙的密码学可以用来创造匿名货币。最成功的例子是比特币,一个由中本聪(Satoshi Nakamoto)发明的方案,于2009年作为开源软件发布。(中本聪的真实身份不得而知,这是一个不同寻常的匿名成功的例子。)

尝试比特币技术其实很容易,Bitcoin.org网站是一个很好的起点,另外,coindesk.com有优秀的教程信息。此外,还有许多相关书籍和在线课程

苹果软件使用用户提供而苹果并不知道的密钥,对运行iOS系统的iPhone的所有内容进行加密。如果政府机构或法官要求苹果解密手机,苹果可以诚实地说它做不到这些。苹果的立场并未赢得政界人士或执法部门的支持,但这是一种站得住脚的立场。这也有商业意义,因为精明的客户不愿意购买政府机构可以轻易窥探内容和通话的手机

即使每个人都努力确保安全,一个坚定的对手总是可以利用4B,即贿赂(bribery)、勒索(blackmail)、盗窃(burglary)、野蛮(brutality)来获得访问权。政府可以用坐牢来威胁那些在被要求泄露密码时拒绝的人。然而,如果你很小心,你可以采取一些体面的措施来保护自己。尽管不总是能抵御所有的威胁,但足以让你在现代世界中正常工作。