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

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

印象深刻

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

发表评论
使用cookie技术保留您的个人信息以便您下次快速评论,继续评论表示您已同意该条款

🎲