《普林斯顿计算机公开课》

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

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

印象深刻

计算机协议中最重要的就是 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代码时(比如遇到 ;还有些标签,如

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

,但在实际中并不需要

。我们的示例使用了结束标签。缩进和换行并非必需,但加上了会让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)来获得访问权。政府可以用坐牢来威胁那些在被要求泄露密码时拒绝的人。然而,如果你很小心,你可以采取一些体面的措施来保护自己。尽管不总是能抵御所有的威胁,但足以让你在现代世界中正常工作。


《普林斯顿计算机公开课》
https://www.frytea.com/post/20240630125119.html
作者
Tianlun Song
发布于
2024年6月30日
更新于
2024年7月3日
许可协议