《编程机制探析》第三章 计算机运行结构
前文反复提到计算机的“硬件”和“软件”这两个概念。那么,硬件和软件到底是什么东西呢?通俗来讲(即,用我们老百姓的话来讲),硬盘中存储的数据叫做软件,除此之外,计算机所有其他的部件,全都叫做硬件。注意,硬盘本身也是硬件。因此,软件是存储在硬件中的。软件必须依托于硬件,才有依托之地。
打一个不太恰当的比方,硬件相当于看得见、摸得着的物质文明,而软件就相当于看不见、摸不着的精神文明。
当然,上述说法只是直观的说法,不是专业的说法。那么,什么是专业的说法呢?专业的说法,必然要涉及到计算机体系结构。提到计算机体系结构,就不得不提到冯.诺依曼结构。
正如本书不会对图灵机展开详述一样,本书也不会对冯.诺依曼结构展开详述。当年上学时,课本中关于图灵机模型和冯.诺依曼结构的篇章可着实不少。但是,现在回头去看,那两种模型结构,更多的是一种纪念上的意义,加上一点理论基础上的溯源意义。所以,本书不会详述这两种模型结构。不过,总线结构却是不得不提的。
总线结构,是现代计算机仍然遵守的一种计算机体系结构。总线,是从英文“Bus”翻译过来的,本意是公共汽车的意思。我不明白,为什么要把这个词翻译成总线。也许是“公交总线”的意思?反正前辈们这么翻译了,咱们也就约定俗成,姑且这么用着。
总线(Bus)结构就像一条公交线路一样,所有的计算机部件全都挂在一条公交总线上,所有的数据交流都通过这条公交线路来进行。每个计算机部件就相当于这条公交总线上的一个公交站点,数据就相当于乘客,从各个公交站点乘坐公共汽车,上上下下,从一个站点到达另一个站点,即从一个硬件部件,到达另一个硬件部件。
在这个总线模型中,每一个公交站点都代表一个硬件部件,公交线路本身——即总线本身——也是一个硬件部件。那么,在现实中,总线对应计算机的哪个硬件呢?
用过台式机、并打开过机箱的读者一定会有印象,机箱中那些东西像公交线路呢?那一条条的宽宽扁扁的塑料数据线,看起来就像一条条公交线路,难道那些就是总线?
我以前就是这么以为的。但是,很遗憾,这个答案是不对的。那些扁扁的塑料线,并非总线,最多只能算是支线。那么,总线到底是什么呢?
急性的读者一定喊起来了,你废什么话呢?还不赶紧说!好吧,我说,总线,其实就是机箱最大的部件——主板,英文叫做“Mother Board”,因此也可以翻译为母板。
所有的其他硬件,包括极为重要的CPU(中央处理单元)、内存、网卡等,全都插在主板上,其他的重要部件,如硬盘、光驱等,也都是通过塑料数据线连接到主板上。主板,就是一个信息交流中心,垄断着所有的信息交换通路。任何两个硬件部件的信息交流,都在主板的掌握之下。
总线结构在信息交换速度和数量上是有瓶颈的。很多计算机科学家都在研究如何打破这个瓶颈。也许,将来的计算机就是非总线结构的了。不过,目前我们使用的计算机大部分还是总线结构,所以,我们还是要对它有一点了解。
读者如果想对计算机主板结构(即总线结构)了解更多的话,除了拆机直接察看之外,还可以用“鲁大师”等免费硬件检测软件察看计算机硬件的具体参数。如果你需要自己安装系统中的各种硬件驱动程序的话,这类工具就很有用。
关于硬件部分,就说这么多了,本书还是主要关注于软件方面。前面讲了,软件就是硬盘上存储的数据。这话并不完整。因为,硬盘并不是唯一可以存储数据的地方,光盘、U盘等存储介质中,同样可以存储数据,自然也可以存放软件。只是为了简化问题起见,我们现在只考虑硬盘这个最重要的数据存储结构。
从广义的概念上讲,所有的数据都可以看做是软件。从狭义的概念上讲,只有可以运行的那部分数据(即计算机能够理解并执行的程序)才可以称为软件。不管从那个概念上来讲,软件都是数据的一种。
在硬盘上,所有的数据都以文件的形式存储。因此,我们通常也把硬盘等存储介质中存放的数据叫做文件。软件程序也不例外,也是以文件的形式存储在硬盘中。那么,这些程序文件到底是如何运行起来的呢?这里面涉及到相当多的基础概念,即使是计算机业内人士,也不见得能够把整个过程理得通顺。
比如我自己,从学校毕业出来之后,脑海里积攒了一堆什么“进程”、“线程”、“操作系统调度”之类的建立在理论模型上的词语,却不知其具体对应物。以为工作中自然会了解这些具体知识。但我发现,工作中更不需要理解这些知识,只要按照前人搭好的框架,往里面添砖加瓦就可以了。
我举一个非常切实的例子,在现实的很多互联网公司中,很多程序员(也包括以前的我),编了几年的互联网程序,却对互联网最重要的协议——HTTP——的基本原理一窍不通。这一方面说明了现代软件公司在软件开发分工管理方面的成功之处,一方面说明了程序员们本身对于基础知识并不重视。说实在的,如果只是谋一份饭碗的话,基础知识确实没太大用处,不知道也照样编程。
但是,如果你像我一样,什么事都想寻根问底,如果不了解原理心里就不踏实的话,本书对你来说就很有用了。
在我真正理解计算机内部运行机理之前,我心里总是感到不踏实,总觉得那里面有一种我没有完全了解的机制在起作用,说不定什么时候,它就会冒出来给我找麻烦。于是,我总是想弄明白它。后来,我把理论模型和现实的计算机部件结合了起来,心里立马就有底了,不再胡思乱想,最终获得了心灵的宁静。(我差点就用了“心灵的救赎”这个词。没办法,受知音体影响太大。)
现在,我们来审视硬盘上的程序文件,看看它是如何运行起来的。
硬盘上存储的一份份程序文件,从概念上来说,都是给计算机阅读的一份份工作流程。对于计算机来说,硬盘就是一个巨大的文件存储仓库,里面存放了很多数据文件,还有很多工作流程文件(即程序)。当计算机工作的时候,它首先要选择一份工作流程,然后按照这份工作流程来工作。在按章办事的过程中,它可能会根据流程规定,从硬盘这个文件存放仓库中取出另外的一份或者多份文件。
现在,有一个问题。当我们说计算机在工作的时候,这个“计算机”到底是指谁?是指包括显示器和机箱在内的整个计算机吗?宽泛来讲,是的。严格来讲,不是。
从严格意义上来讲,计算机中真正干活的硬件部件,只有一个。那就是中央处理单元CPU。只有CPU才有计算功能,只有CPU才有理解并执行计算机程序的能力。这也是为什么它叫做“中央”处理单元的缘故。舍它其谁?
注:在一些高性能显卡中,有GPU(图形处理单元)的配置,也带有了一点CPU的运算能力,我们可以把GPU看作是一种专门处理图形显示的特殊CPU。
CPU芯片的个头并不大,在主板上只占有一小块位置。但是,其地位却是最重要的。主板上专门有一个风扇为它扇风。生怕它太热太疲劳。没办法,谁让人家劳苦功高呢。整个机箱中,就它一个人在真正干活。
CPU有自己的工作台,学名叫做“寄存器”。但是,这个叫做“寄存器”的工作台太小,而硬盘那个文件存储仓库中的每个文件都是那么的巨大,工作台上根本就放不下。于是,CPU就在工作台旁边放了一个巨大的木架,叫做“内存”。
CPU工作的时候,先把需要执行的工作流程从硬盘那个文件存储仓库中取出来,放到内存那个木架上。内存这个木架足够大,可以放得下巨大的文件。然后,CPU就把内存中的文件数据,一部分一部分地取出来,放在自己的“寄存器”工作台上,进行处理。这个动作叫做“取”,或者叫做“读取”。CPU做完手头工作之后,就把工作台上的工作结果放回到内存这个木架上。这个动作叫做“存”,或者叫做“写入”。
在CPU硬件直接支持的汇编语言中,存和取这对指令实际上是用一个数据传送指令来表达,只需要正确地定义源地址和目标地址,就可以正确地实现存取动作。
现在,我们就知道前面讲的“存取”这一对动作的含义了。取,是指从内存中取到寄存器中。存,是指从寄存器中存到内存中。
除了寄存器和内存之外,CPU也可以存取包括硬盘在内的各种存储空间。为了简化问题起见,我们目前可以暂且认为,CPU工作的时候有三个存取空间。第一个存取空间是CPU芯片内部的空间,叫做寄存器;第二个存取空间是插在主板上的内存;第三个存取空间是通过塑料数据线与主板相连的硬盘。
CPU存取这三个空间的速度,与这三个空间的距离成反比。寄存器就在CPU内部,自然是最快的。内存插在主板上,距离远一点,就慢一点。硬盘就更远了,还要通过一条线连接在主板上,存取速度最慢。
当我们说“存”、“取”、“读”、“写”的时候,我们都是以CPU的视点角度来说的。把数据从离CPU远的地方,传输到离CPU近的地方,就叫做“读”或者“取”。把数据从离CPU近的地方,传输到离CPU远的地方,就叫做“存”或者“写”。
比如,数据从寄存器传送到内存,叫做“存”或者“写”;数据从内存传到寄存器,叫做“读”或者“取”。
数据从寄存器传送到硬盘,叫做“存”或者“写”;数据从硬盘传到寄存器,叫做“读”或者“取”。
数据从内存传送到硬盘,叫做“存”或者“写”;数据从硬盘传到内存,叫做“读”或者“取”。
喜欢较真的读者一定会问,如果数据从寄存器传到寄存器呢?从内存传到内存呢?从硬盘传到硬盘呢?如果是这些情况,那怎么叫都行,或者干脆就叫数据传输。
需要注意的一点是,在CPU真正的工作过程中,所有的数据存取都要经过“寄存器”这个工作台。不管数据从哪儿到哪儿,“寄存器”都是必由之路。
如果我们需要把硬盘中的数据读入到内存中,CPU首先需要把数据从硬盘中读入到寄存器中,然后再写入到内存中。
如果我们需要把内存中的数据写入到硬盘中的时候,CPU首先需要先把数据从内存中读入道寄存器中,然后再写入到硬盘中。
当CPU把程序文件从硬盘中读取到内存中的时候(经由寄存器),这时候,程序就发生了质的变化——它不再是硬盘上僵化的数据,它获得了生命力,它成为了进程。这个唤醒的过程很奇妙,就像童话《睡美人》中的那个王子的神奇一吻一样,沉睡的公主一下子就醒了。
一个程序只有在进入到内存成为进程之后,它才能被CPU执行。进程的英文叫做Process,意思是处理流程。进程的概念在软件编程中极为重要。可以说,软件的整个编程模型就是建立在进程基础上的。
一个程序进入到内存之后,就会成为一个进程。两个程序进入到内存之后,就会成为两个进程。对于支持多进程的应用程序来说,你运行这个程序两次,就会产生两个进程。这个概念一定要弄清楚。
下面以占据了个人计算机市场大半江山的Windows操作系统为例。
“什么是操作系统?”有些读者可能会问,“听起来好像挺酷的样子。”
其实,操作系统也是硬盘上的程序,进入到内存之后,同样会变成进程,只不过,操作系统是计算机中最先运行起来的程序,而且一直运行下去,直到关机。内存中其他的进程全都是操作系统进程产生的。另外,操作系统进程并非是一个独立进程,而是一类进程,包括了很多系统服务进程。
在Windows操作系统中,你可以启动“任务管理器”来查看系统中产生的进程。具体启用方法是用鼠标右键点击桌面下方的“任务栏”,就可以看到“任务管理器”的选项。或者,你一起按键盘上的CTRL + ALT + DELETE这三个键的组合,也可以启动任务管理器。
你在桌面上,启动“Internet Explorer”(IExplore.exe)这个应用程序,你就可以在任务管理器中看到IExplore.exe这个进程。你再次在桌面上启动“Internet Explorer”(IExplore.exe)这个应用程序,你就可以在任务管理器中看到两个IExplore.exe进程。
我们在任务管理器中可以看到,计算机内存中可以同时存在多个进程。那么,这是否意味着多个进程可以同时运行呢?不,不是这样的。计算机的整个体系结构中,真正干活的只有CPU一个人。而CPU同一时间只能做一件事情,即,CPU同一时间内,只能运行一个进程。其他的进程只能以悬停僵止状态,在内存中苦苦等待,宛若罪人在地狱中仰望天堂,盼望着CPU之光能够照耀到自己,从而复苏得救赎,获得一小段运行的机会。
现在的CPU已经进入多核时代,一个CPU可能拥有几个处理核心,也最多可以同时运行几个进程,其他大部分进程仍然得苦苦等待。
对于一个进程来说,它的生命历程就是这样:从它被CPU从硬盘中唤醒到内存中开始,它就开始了漫长的等待。在等待的过程中,它每隔一小段时间就获得一点运行的机会,然后,它又被挂了起来,继续等待。这样的过程周而复始,直到它运行结束。
在计算机内存所有的进程中,有一类进程极为特殊。这类进程在计算机开机的时候,就最先运行,而且将一直运行下去,直到关机。这类进程就是操作系统进程,包括各种基本的系统服务进程,比如,图形桌面程序,网络服务程序,硬件驱动程序,等等。
不仅系统程序如此,几乎所有的图形界面应用程序也都是如此。一旦跑起来,就一直往复运行,等候用户(这里的用户既包括人类用户,也包括内存中的其他进程,比如操作系统进程)的操作信号命令,并给出相应回应。直到用户给出明确的关闭命令,图形界面进程才会选择自行结束自己的生命历程。
在这个过程中,图形界面应用程序大部分时间都是被挂起来了,处于等候状态中,只有少部分时间才处于运行状态。但是,人们感觉不到这一点。对于人类来说,计算机程序的运行周期太快太密集了。一秒钟之内,一个计算机进程可以反反复复停顿、运行、停顿、运行成千上万次,而人们却豪无所觉。因此,即使一个进程大部分时间都处于等待状态,也足以及时应答人类的操作。
当然,如果计算机内存里进程过多,或者某些进程占用CPU时间过长,计算机也会反应不过来,整个系统处于停顿状态。这时,人们就会抱怨:“这破计算机,怎么这么慢呀。又死机了。应该换个更快的新机子了。”
其实,不是计算机不够快,而是因为人机交互这种(人与计算机之间的应答)应用模式并不符合计算机的工作特性。
最适合计算机的工作方式应该是这样:人们先花一段时间,把所有的工作流程都编制好,一次全都交给计算机,然后,计算机就开始不受干扰地按照既定程序独自运行。
不过,计算机毕竟是人类发明的工具,是要为人类服务的。即使它本性上并不适合交互式工作方式,人们还是要让它按照这种工作方式。典型的例子就是图形界面程序,大部分时间并不是在计算,而是在空转,偶尔刷新一下屏幕上的对应区域。
我们可以总结出一个图形界面应用程序的大致工作流程:
第一步,创建一系列图形界面(学名叫做“窗口”)。
第二步,查看是否有来自于用户的操作命令信号。如果没有,跳转到第二步,重复执行本步骤;如果有,执行第三步。
第三步,来自于用户的操作信号命令是否为“刷新图形界面”?如果是,那么刷新本进程在屏幕上对应位置的图形界面部分。
注:“刷新图形界面”这个信号是由操作系统进程发出的。液晶屏的刷新频率一般是六十赫兹,那么,每隔六十分之一秒,图形界面进程就会接受到一次“刷新图形界面”的信号。
第四步,来自于用户的操作命令信号是否为“关闭”?如果是,那么结束本进程。如果不是,那么,根据用户命令执行不同的任务。完成后,跳转到第二步。
基本上所有的图形界面应用程序全都遵守这么一套工作流程,就连操作系统本身也不例外。
至于那些没有图形界面的系统服务程序,也遵守大致的工作的流程,只不过在第一步的时候,不需要创建图形界面而已。
就这样,包括操作系统在内的所有进程都在内存中反反复复地重复着同样的工作流程——查看用户命令,没有查到,再查一遍,还是没有查到,再查一遍……查了不知多少遍之后,这个进程停止了运行。
风水轮流转,另一个进程开始运行。那个开始运行的幸运儿也重复着同样的命运。查看用户命令,没有查到,再查一遍,还是没有查到,再查一遍……
我们可以看到,计算机进程在大部分时间里,不是在空转,就是在等待。就这样,计算机的大部分计算能力实际上是被白白浪费了。只有亿万分之一的计算能力才真正用到了。其余的计算能力,都在漫长的等待中徒然消耗掉了。
千年等一回,都不足以表达这种等待的漫长。对于一个进程来说,可能在数亿次的轮回之后,才可能等到用户的一个命令信息。用佛家的话来说,这都是缘分。五百年的擦肩,才能换来一次回眸。五百亿年的等候,才能等来高等生物(即人类,一种生物钟比计算机时钟慢了千万倍的一种智能生物)的一次眷顾。
美国科幻小说家弗诺•文奇写了一本叫做《循环》的科幻小说,把计算机程序的这种周而复始、反复运行的轮回特性,描述得淋漓尽致。
科幻小说《循环》的大致情节如下:
一个神通广大的博士,发明了量子计算机。这个博士谋杀了一些高智商高学历的人(其中有些是他的学生),并把这些人的头脑部分装载到量子计算机中,作为计算机程序一样运行。这样,博士就相当于拥有了一批功能极为强大的高级人工智能程序。
这些被装载到量子计算机中的“人”(实际上已经成为计算机虚拟世界中的虚拟人格)并没有意识到自己已经死亡。他们以为自己还活在现实世界中,他们继续在虚拟的环境中工作着。
虚拟人格环境和现实世界的时钟频率完全不同。计算机的运行频率远远超过现实世界的频率。现实世界的短短一秒钟之内,虚拟人格可能会经历千万次生命周期循环。正如中国古代神话中的“山中方七日,世上已千年”。
就这样,博士拥有了一批极为高效、极为聪明的免费打工者。这些打工者中,最聪明的那些人,主要做一些虚拟世界研发方面的工作,不断地完善量子计算机中的虚拟世界,使之更为真实。可悲的是,那些最聪明的头脑,并没有意识到,自己的工作成果正在使囚禁自己的牢笼越来越坚固。
还有一部分人做高级客户服务工作。专门处理客户的邮件,分析客户的需求,根据各种资料,作出方案和答复。这是真正意义上的人工智能。现实世界的客户看到的回信都是充满了人情味和人性特点的文字。
由于这些虚拟人格的运转速度如此之快,工作效率如此之高。他们足以服务全世界的客户。可以想见,博士因此赚了多少钱。
虚拟办公楼群中的所有办公人员都没有意识到自己生活在虚拟的环境中。他们每天早晨醒来,都以为自己是第一天上班,于是精神状态饱满地开始一天的工作。晚上下班后,系统就会重启他们,进入下一次工作循环。于是他们再一次醒来,再一次以为自己是第一天上班,再一次精神饱满地开始一天的工作。
注意,这里的“一天”是指系统中的一天,而不是现实的一天。系统中的“一天”可能只是现实中的弹指一挥间。
虚拟人格的生命周期只有一天,而且永远是同一天。他们的记忆永远保留在那一天。除了工作经验的增长,所有的关于人生的记忆,永远就只有那么一天,工作效率最高的一天。
虚拟人格一天一天地工作着,等待着下班后去聚会娱乐,但是永远等不到那一刻,每次下班后就是重启,新的一天开始。
博士的算盘打得很精。这些虚拟人格永远不会有机会发现自己生活在虚拟环境中。更何况,虚拟环境正在虚拟研究员的努力工作下,变得越来越完善,越来越真实。
但是,凡事都有意外。不是吗?小说的情节,就是依靠无数意外驱动的。本故事也不例外。某一天,意外发生了。
一位客户服务人员(一位女士)在处理客户邮件的时候,突然收到一封匿名骚扰信件。这封信件里面透露了这位女士童年时代的一件隐私。该女士大怒,开始寻找是谁发了这封无聊的信。从一个楼找到另一个楼,最后找到了研究所,遇到了那群研究员。他们见面之后,进行了一番交谈,感到非常惊讶。他们发现,他们认为自己生活在不同的日子里面,整整差了几个月。在经过一番对各种蛛丝马迹的探讨,一个最聪明的研究员终于推论出,他们生活在一个虚拟世界中。
原来,这个最聪明的研究员在一次偶然的情况下,发现了自己这群人生活在虚拟环境中的事实。他把自己的惊人发现用加密的方式保留在系统日志文件中,散落在各个地方。以免被博士的系统检查工具发现。
但是研究员没有任何办法把这个发现保留在自己的记忆中,只要过了这一天,他的记忆就会被重启。他就会完全忘记有这么一回事。他需要一个线索,一个触发器来提醒自己。
这位女士就是触发器。研究员正巧遇到这位女士,询问这个女士的一件童年隐私。然后把一封匿名信件埋藏在邮件系统中。这封信件有千万分之一的概率会被触发,寄到该女士的信箱。这样的概率,可以小到不为人注意和发现。
该女士收到这封信之后,大概有二分之一的机会,会勃然大怒,去寻找发信人。另有十分之一的机会,会在太阳落山之前(即系统重启之前)能够顺利找到研究所。而见到研究员之后,研究员可能只有百分之一的机会,能够按图索骥,根据现有线索找到并解密自己以前存放的记录,从而再次发现“自己生活在虚拟环境”的事实。每次发现这个事实,研究员就会在记录中多添加一些信息。
可以看出,研究员重新发现“虚拟环境”的概率是非常非常小的。小到可以忽略不计。但是,虚拟人格有着天生的优势,那就是生命周期循环非常快。现实世界的一秒钟,虚拟环境中可能已经过了千秋万代。
因此,早晚有一天,研究员能够储备足够的信息,能够有足够的时间,突破系统的限制,揭发博士的罪行。
故事的末尾,研究员等人发现了“自己是虚拟人格”之后,赶紧做了一些突破系统的准备工作。但是,窗外的太阳就快下山了,新的循环又要开始了。刚刚寻找回来的记忆,又将很快丧失。只能等到下一次偶遇,才能够找回自己的记忆。
如果对本故事还有什么难以理解的地方,那么可以这么想象:一个人不断地轮回重生,每次都要喝孟婆汤,忘记前生的记忆。为了找到前生的记忆,他必须找到他前生存放在某个地方的日记。大部分时间,他是找不到的。偶尔,他会找到一次。他就会抓紧时间,阅读前生的日记,并添加新的内容,渴望有一天,能够利用自己积累的这些信息勘破轮回,跳出五行之外。
这个故事给了我们什么启迪呢?那就是,一定要注意经常杀毒。故事中,虚拟系统中那个最聪明的研究员,居然在暗中做手脚,偷偷篡改系统文件,用于存取自己的记忆。这明显是计算机病毒的行为。如果那个博士能够预见到这一点的话,就应该给那些研究员分配一些研制杀毒软件的工作,专门用来对付这种病毒行为。这叫做以夷制夷,以毒攻毒。
当然,在这个故事中,“病毒”是正义的一方。正如在电影《黑客帝国》(Matrix)中,那些入侵系统的外来人类,实际上也是病毒的角色,却代表着正义,与系统对抗。
和《循环》不一样,《黑客帝国》中的虚拟系统运行得比较缓慢,与人类的生命周期基本一致。这也许是系统为了适应人类的生物钟而进行的自我调节。
前文反复提到计算机的“硬件”和“软件”这两个概念。那么,硬件和软件到底是什么东西呢?通俗来讲(即,用我们老百姓的话来讲),硬盘中存储的数据叫做软件,除此之外,计算机所有其他的部件,全都叫做硬件。注意,硬盘本身也是硬件。因此,软件是存储在硬件中的。软件必须依托于硬件,才有依托之地。
打一个不太恰当的比方,硬件相当于看得见、摸得着的物质文明,而软件就相当于看不见、摸不着的精神文明。
当然,上述说法只是直观的说法,不是专业的说法。那么,什么是专业的说法呢?专业的说法,必然要涉及到计算机体系结构。提到计算机体系结构,就不得不提到冯.诺依曼结构。
正如本书不会对图灵机展开详述一样,本书也不会对冯.诺依曼结构展开详述。当年上学时,课本中关于图灵机模型和冯.诺依曼结构的篇章可着实不少。但是,现在回头去看,那两种模型结构,更多的是一种纪念上的意义,加上一点理论基础上的溯源意义。所以,本书不会详述这两种模型结构。不过,总线结构却是不得不提的。
总线结构,是现代计算机仍然遵守的一种计算机体系结构。总线,是从英文“Bus”翻译过来的,本意是公共汽车的意思。我不明白,为什么要把这个词翻译成总线。也许是“公交总线”的意思?反正前辈们这么翻译了,咱们也就约定俗成,姑且这么用着。
总线(Bus)结构就像一条公交线路一样,所有的计算机部件全都挂在一条公交总线上,所有的数据交流都通过这条公交线路来进行。每个计算机部件就相当于这条公交总线上的一个公交站点,数据就相当于乘客,从各个公交站点乘坐公共汽车,上上下下,从一个站点到达另一个站点,即从一个硬件部件,到达另一个硬件部件。
在这个总线模型中,每一个公交站点都代表一个硬件部件,公交线路本身——即总线本身——也是一个硬件部件。那么,在现实中,总线对应计算机的哪个硬件呢?
用过台式机、并打开过机箱的读者一定会有印象,机箱中那些东西像公交线路呢?那一条条的宽宽扁扁的塑料数据线,看起来就像一条条公交线路,难道那些就是总线?
我以前就是这么以为的。但是,很遗憾,这个答案是不对的。那些扁扁的塑料线,并非总线,最多只能算是支线。那么,总线到底是什么呢?
急性的读者一定喊起来了,你废什么话呢?还不赶紧说!好吧,我说,总线,其实就是机箱最大的部件——主板,英文叫做“Mother Board”,因此也可以翻译为母板。
所有的其他硬件,包括极为重要的CPU(中央处理单元)、内存、网卡等,全都插在主板上,其他的重要部件,如硬盘、光驱等,也都是通过塑料数据线连接到主板上。主板,就是一个信息交流中心,垄断着所有的信息交换通路。任何两个硬件部件的信息交流,都在主板的掌握之下。
总线结构在信息交换速度和数量上是有瓶颈的。很多计算机科学家都在研究如何打破这个瓶颈。也许,将来的计算机就是非总线结构的了。不过,目前我们使用的计算机大部分还是总线结构,所以,我们还是要对它有一点了解。
读者如果想对计算机主板结构(即总线结构)了解更多的话,除了拆机直接察看之外,还可以用“鲁大师”等免费硬件检测软件察看计算机硬件的具体参数。如果你需要自己安装系统中的各种硬件驱动程序的话,这类工具就很有用。
关于硬件部分,就说这么多了,本书还是主要关注于软件方面。前面讲了,软件就是硬盘上存储的数据。这话并不完整。因为,硬盘并不是唯一可以存储数据的地方,光盘、U盘等存储介质中,同样可以存储数据,自然也可以存放软件。只是为了简化问题起见,我们现在只考虑硬盘这个最重要的数据存储结构。
从广义的概念上讲,所有的数据都可以看做是软件。从狭义的概念上讲,只有可以运行的那部分数据(即计算机能够理解并执行的程序)才可以称为软件。不管从那个概念上来讲,软件都是数据的一种。
在硬盘上,所有的数据都以文件的形式存储。因此,我们通常也把硬盘等存储介质中存放的数据叫做文件。软件程序也不例外,也是以文件的形式存储在硬盘中。那么,这些程序文件到底是如何运行起来的呢?这里面涉及到相当多的基础概念,即使是计算机业内人士,也不见得能够把整个过程理得通顺。
比如我自己,从学校毕业出来之后,脑海里积攒了一堆什么“进程”、“线程”、“操作系统调度”之类的建立在理论模型上的词语,却不知其具体对应物。以为工作中自然会了解这些具体知识。但我发现,工作中更不需要理解这些知识,只要按照前人搭好的框架,往里面添砖加瓦就可以了。
我举一个非常切实的例子,在现实的很多互联网公司中,很多程序员(也包括以前的我),编了几年的互联网程序,却对互联网最重要的协议——HTTP——的基本原理一窍不通。这一方面说明了现代软件公司在软件开发分工管理方面的成功之处,一方面说明了程序员们本身对于基础知识并不重视。说实在的,如果只是谋一份饭碗的话,基础知识确实没太大用处,不知道也照样编程。
但是,如果你像我一样,什么事都想寻根问底,如果不了解原理心里就不踏实的话,本书对你来说就很有用了。
在我真正理解计算机内部运行机理之前,我心里总是感到不踏实,总觉得那里面有一种我没有完全了解的机制在起作用,说不定什么时候,它就会冒出来给我找麻烦。于是,我总是想弄明白它。后来,我把理论模型和现实的计算机部件结合了起来,心里立马就有底了,不再胡思乱想,最终获得了心灵的宁静。(我差点就用了“心灵的救赎”这个词。没办法,受知音体影响太大。)
现在,我们来审视硬盘上的程序文件,看看它是如何运行起来的。
硬盘上存储的一份份程序文件,从概念上来说,都是给计算机阅读的一份份工作流程。对于计算机来说,硬盘就是一个巨大的文件存储仓库,里面存放了很多数据文件,还有很多工作流程文件(即程序)。当计算机工作的时候,它首先要选择一份工作流程,然后按照这份工作流程来工作。在按章办事的过程中,它可能会根据流程规定,从硬盘这个文件存放仓库中取出另外的一份或者多份文件。
现在,有一个问题。当我们说计算机在工作的时候,这个“计算机”到底是指谁?是指包括显示器和机箱在内的整个计算机吗?宽泛来讲,是的。严格来讲,不是。
从严格意义上来讲,计算机中真正干活的硬件部件,只有一个。那就是中央处理单元CPU。只有CPU才有计算功能,只有CPU才有理解并执行计算机程序的能力。这也是为什么它叫做“中央”处理单元的缘故。舍它其谁?
注:在一些高性能显卡中,有GPU(图形处理单元)的配置,也带有了一点CPU的运算能力,我们可以把GPU看作是一种专门处理图形显示的特殊CPU。
CPU芯片的个头并不大,在主板上只占有一小块位置。但是,其地位却是最重要的。主板上专门有一个风扇为它扇风。生怕它太热太疲劳。没办法,谁让人家劳苦功高呢。整个机箱中,就它一个人在真正干活。
CPU有自己的工作台,学名叫做“寄存器”。但是,这个叫做“寄存器”的工作台太小,而硬盘那个文件存储仓库中的每个文件都是那么的巨大,工作台上根本就放不下。于是,CPU就在工作台旁边放了一个巨大的木架,叫做“内存”。
CPU工作的时候,先把需要执行的工作流程从硬盘那个文件存储仓库中取出来,放到内存那个木架上。内存这个木架足够大,可以放得下巨大的文件。然后,CPU就把内存中的文件数据,一部分一部分地取出来,放在自己的“寄存器”工作台上,进行处理。这个动作叫做“取”,或者叫做“读取”。CPU做完手头工作之后,就把工作台上的工作结果放回到内存这个木架上。这个动作叫做“存”,或者叫做“写入”。
在CPU硬件直接支持的汇编语言中,存和取这对指令实际上是用一个数据传送指令来表达,只需要正确地定义源地址和目标地址,就可以正确地实现存取动作。
现在,我们就知道前面讲的“存取”这一对动作的含义了。取,是指从内存中取到寄存器中。存,是指从寄存器中存到内存中。
除了寄存器和内存之外,CPU也可以存取包括硬盘在内的各种存储空间。为了简化问题起见,我们目前可以暂且认为,CPU工作的时候有三个存取空间。第一个存取空间是CPU芯片内部的空间,叫做寄存器;第二个存取空间是插在主板上的内存;第三个存取空间是通过塑料数据线与主板相连的硬盘。
CPU存取这三个空间的速度,与这三个空间的距离成反比。寄存器就在CPU内部,自然是最快的。内存插在主板上,距离远一点,就慢一点。硬盘就更远了,还要通过一条线连接在主板上,存取速度最慢。
当我们说“存”、“取”、“读”、“写”的时候,我们都是以CPU的视点角度来说的。把数据从离CPU远的地方,传输到离CPU近的地方,就叫做“读”或者“取”。把数据从离CPU近的地方,传输到离CPU远的地方,就叫做“存”或者“写”。
比如,数据从寄存器传送到内存,叫做“存”或者“写”;数据从内存传到寄存器,叫做“读”或者“取”。
数据从寄存器传送到硬盘,叫做“存”或者“写”;数据从硬盘传到寄存器,叫做“读”或者“取”。
数据从内存传送到硬盘,叫做“存”或者“写”;数据从硬盘传到内存,叫做“读”或者“取”。
喜欢较真的读者一定会问,如果数据从寄存器传到寄存器呢?从内存传到内存呢?从硬盘传到硬盘呢?如果是这些情况,那怎么叫都行,或者干脆就叫数据传输。
需要注意的一点是,在CPU真正的工作过程中,所有的数据存取都要经过“寄存器”这个工作台。不管数据从哪儿到哪儿,“寄存器”都是必由之路。
如果我们需要把硬盘中的数据读入到内存中,CPU首先需要把数据从硬盘中读入到寄存器中,然后再写入到内存中。
如果我们需要把内存中的数据写入到硬盘中的时候,CPU首先需要先把数据从内存中读入道寄存器中,然后再写入到硬盘中。
当CPU把程序文件从硬盘中读取到内存中的时候(经由寄存器),这时候,程序就发生了质的变化——它不再是硬盘上僵化的数据,它获得了生命力,它成为了进程。这个唤醒的过程很奇妙,就像童话《睡美人》中的那个王子的神奇一吻一样,沉睡的公主一下子就醒了。
一个程序只有在进入到内存成为进程之后,它才能被CPU执行。进程的英文叫做Process,意思是处理流程。进程的概念在软件编程中极为重要。可以说,软件的整个编程模型就是建立在进程基础上的。
一个程序进入到内存之后,就会成为一个进程。两个程序进入到内存之后,就会成为两个进程。对于支持多进程的应用程序来说,你运行这个程序两次,就会产生两个进程。这个概念一定要弄清楚。
下面以占据了个人计算机市场大半江山的Windows操作系统为例。
“什么是操作系统?”有些读者可能会问,“听起来好像挺酷的样子。”
其实,操作系统也是硬盘上的程序,进入到内存之后,同样会变成进程,只不过,操作系统是计算机中最先运行起来的程序,而且一直运行下去,直到关机。内存中其他的进程全都是操作系统进程产生的。另外,操作系统进程并非是一个独立进程,而是一类进程,包括了很多系统服务进程。
在Windows操作系统中,你可以启动“任务管理器”来查看系统中产生的进程。具体启用方法是用鼠标右键点击桌面下方的“任务栏”,就可以看到“任务管理器”的选项。或者,你一起按键盘上的CTRL + ALT + DELETE这三个键的组合,也可以启动任务管理器。
你在桌面上,启动“Internet Explorer”(IExplore.exe)这个应用程序,你就可以在任务管理器中看到IExplore.exe这个进程。你再次在桌面上启动“Internet Explorer”(IExplore.exe)这个应用程序,你就可以在任务管理器中看到两个IExplore.exe进程。
我们在任务管理器中可以看到,计算机内存中可以同时存在多个进程。那么,这是否意味着多个进程可以同时运行呢?不,不是这样的。计算机的整个体系结构中,真正干活的只有CPU一个人。而CPU同一时间只能做一件事情,即,CPU同一时间内,只能运行一个进程。其他的进程只能以悬停僵止状态,在内存中苦苦等待,宛若罪人在地狱中仰望天堂,盼望着CPU之光能够照耀到自己,从而复苏得救赎,获得一小段运行的机会。
现在的CPU已经进入多核时代,一个CPU可能拥有几个处理核心,也最多可以同时运行几个进程,其他大部分进程仍然得苦苦等待。
对于一个进程来说,它的生命历程就是这样:从它被CPU从硬盘中唤醒到内存中开始,它就开始了漫长的等待。在等待的过程中,它每隔一小段时间就获得一点运行的机会,然后,它又被挂了起来,继续等待。这样的过程周而复始,直到它运行结束。
在计算机内存所有的进程中,有一类进程极为特殊。这类进程在计算机开机的时候,就最先运行,而且将一直运行下去,直到关机。这类进程就是操作系统进程,包括各种基本的系统服务进程,比如,图形桌面程序,网络服务程序,硬件驱动程序,等等。
不仅系统程序如此,几乎所有的图形界面应用程序也都是如此。一旦跑起来,就一直往复运行,等候用户(这里的用户既包括人类用户,也包括内存中的其他进程,比如操作系统进程)的操作信号命令,并给出相应回应。直到用户给出明确的关闭命令,图形界面进程才会选择自行结束自己的生命历程。
在这个过程中,图形界面应用程序大部分时间都是被挂起来了,处于等候状态中,只有少部分时间才处于运行状态。但是,人们感觉不到这一点。对于人类来说,计算机程序的运行周期太快太密集了。一秒钟之内,一个计算机进程可以反反复复停顿、运行、停顿、运行成千上万次,而人们却豪无所觉。因此,即使一个进程大部分时间都处于等待状态,也足以及时应答人类的操作。
当然,如果计算机内存里进程过多,或者某些进程占用CPU时间过长,计算机也会反应不过来,整个系统处于停顿状态。这时,人们就会抱怨:“这破计算机,怎么这么慢呀。又死机了。应该换个更快的新机子了。”
其实,不是计算机不够快,而是因为人机交互这种(人与计算机之间的应答)应用模式并不符合计算机的工作特性。
最适合计算机的工作方式应该是这样:人们先花一段时间,把所有的工作流程都编制好,一次全都交给计算机,然后,计算机就开始不受干扰地按照既定程序独自运行。
不过,计算机毕竟是人类发明的工具,是要为人类服务的。即使它本性上并不适合交互式工作方式,人们还是要让它按照这种工作方式。典型的例子就是图形界面程序,大部分时间并不是在计算,而是在空转,偶尔刷新一下屏幕上的对应区域。
我们可以总结出一个图形界面应用程序的大致工作流程:
第一步,创建一系列图形界面(学名叫做“窗口”)。
第二步,查看是否有来自于用户的操作命令信号。如果没有,跳转到第二步,重复执行本步骤;如果有,执行第三步。
第三步,来自于用户的操作信号命令是否为“刷新图形界面”?如果是,那么刷新本进程在屏幕上对应位置的图形界面部分。
注:“刷新图形界面”这个信号是由操作系统进程发出的。液晶屏的刷新频率一般是六十赫兹,那么,每隔六十分之一秒,图形界面进程就会接受到一次“刷新图形界面”的信号。
第四步,来自于用户的操作命令信号是否为“关闭”?如果是,那么结束本进程。如果不是,那么,根据用户命令执行不同的任务。完成后,跳转到第二步。
基本上所有的图形界面应用程序全都遵守这么一套工作流程,就连操作系统本身也不例外。
至于那些没有图形界面的系统服务程序,也遵守大致的工作的流程,只不过在第一步的时候,不需要创建图形界面而已。
就这样,包括操作系统在内的所有进程都在内存中反反复复地重复着同样的工作流程——查看用户命令,没有查到,再查一遍,还是没有查到,再查一遍……查了不知多少遍之后,这个进程停止了运行。
风水轮流转,另一个进程开始运行。那个开始运行的幸运儿也重复着同样的命运。查看用户命令,没有查到,再查一遍,还是没有查到,再查一遍……
我们可以看到,计算机进程在大部分时间里,不是在空转,就是在等待。就这样,计算机的大部分计算能力实际上是被白白浪费了。只有亿万分之一的计算能力才真正用到了。其余的计算能力,都在漫长的等待中徒然消耗掉了。
千年等一回,都不足以表达这种等待的漫长。对于一个进程来说,可能在数亿次的轮回之后,才可能等到用户的一个命令信息。用佛家的话来说,这都是缘分。五百年的擦肩,才能换来一次回眸。五百亿年的等候,才能等来高等生物(即人类,一种生物钟比计算机时钟慢了千万倍的一种智能生物)的一次眷顾。
美国科幻小说家弗诺•文奇写了一本叫做《循环》的科幻小说,把计算机程序的这种周而复始、反复运行的轮回特性,描述得淋漓尽致。
科幻小说《循环》的大致情节如下:
一个神通广大的博士,发明了量子计算机。这个博士谋杀了一些高智商高学历的人(其中有些是他的学生),并把这些人的头脑部分装载到量子计算机中,作为计算机程序一样运行。这样,博士就相当于拥有了一批功能极为强大的高级人工智能程序。
这些被装载到量子计算机中的“人”(实际上已经成为计算机虚拟世界中的虚拟人格)并没有意识到自己已经死亡。他们以为自己还活在现实世界中,他们继续在虚拟的环境中工作着。
虚拟人格环境和现实世界的时钟频率完全不同。计算机的运行频率远远超过现实世界的频率。现实世界的短短一秒钟之内,虚拟人格可能会经历千万次生命周期循环。正如中国古代神话中的“山中方七日,世上已千年”。
就这样,博士拥有了一批极为高效、极为聪明的免费打工者。这些打工者中,最聪明的那些人,主要做一些虚拟世界研发方面的工作,不断地完善量子计算机中的虚拟世界,使之更为真实。可悲的是,那些最聪明的头脑,并没有意识到,自己的工作成果正在使囚禁自己的牢笼越来越坚固。
还有一部分人做高级客户服务工作。专门处理客户的邮件,分析客户的需求,根据各种资料,作出方案和答复。这是真正意义上的人工智能。现实世界的客户看到的回信都是充满了人情味和人性特点的文字。
由于这些虚拟人格的运转速度如此之快,工作效率如此之高。他们足以服务全世界的客户。可以想见,博士因此赚了多少钱。
虚拟办公楼群中的所有办公人员都没有意识到自己生活在虚拟的环境中。他们每天早晨醒来,都以为自己是第一天上班,于是精神状态饱满地开始一天的工作。晚上下班后,系统就会重启他们,进入下一次工作循环。于是他们再一次醒来,再一次以为自己是第一天上班,再一次精神饱满地开始一天的工作。
注意,这里的“一天”是指系统中的一天,而不是现实的一天。系统中的“一天”可能只是现实中的弹指一挥间。
虚拟人格的生命周期只有一天,而且永远是同一天。他们的记忆永远保留在那一天。除了工作经验的增长,所有的关于人生的记忆,永远就只有那么一天,工作效率最高的一天。
虚拟人格一天一天地工作着,等待着下班后去聚会娱乐,但是永远等不到那一刻,每次下班后就是重启,新的一天开始。
博士的算盘打得很精。这些虚拟人格永远不会有机会发现自己生活在虚拟环境中。更何况,虚拟环境正在虚拟研究员的努力工作下,变得越来越完善,越来越真实。
但是,凡事都有意外。不是吗?小说的情节,就是依靠无数意外驱动的。本故事也不例外。某一天,意外发生了。
一位客户服务人员(一位女士)在处理客户邮件的时候,突然收到一封匿名骚扰信件。这封信件里面透露了这位女士童年时代的一件隐私。该女士大怒,开始寻找是谁发了这封无聊的信。从一个楼找到另一个楼,最后找到了研究所,遇到了那群研究员。他们见面之后,进行了一番交谈,感到非常惊讶。他们发现,他们认为自己生活在不同的日子里面,整整差了几个月。在经过一番对各种蛛丝马迹的探讨,一个最聪明的研究员终于推论出,他们生活在一个虚拟世界中。
原来,这个最聪明的研究员在一次偶然的情况下,发现了自己这群人生活在虚拟环境中的事实。他把自己的惊人发现用加密的方式保留在系统日志文件中,散落在各个地方。以免被博士的系统检查工具发现。
但是研究员没有任何办法把这个发现保留在自己的记忆中,只要过了这一天,他的记忆就会被重启。他就会完全忘记有这么一回事。他需要一个线索,一个触发器来提醒自己。
这位女士就是触发器。研究员正巧遇到这位女士,询问这个女士的一件童年隐私。然后把一封匿名信件埋藏在邮件系统中。这封信件有千万分之一的概率会被触发,寄到该女士的信箱。这样的概率,可以小到不为人注意和发现。
该女士收到这封信之后,大概有二分之一的机会,会勃然大怒,去寻找发信人。另有十分之一的机会,会在太阳落山之前(即系统重启之前)能够顺利找到研究所。而见到研究员之后,研究员可能只有百分之一的机会,能够按图索骥,根据现有线索找到并解密自己以前存放的记录,从而再次发现“自己生活在虚拟环境”的事实。每次发现这个事实,研究员就会在记录中多添加一些信息。
可以看出,研究员重新发现“虚拟环境”的概率是非常非常小的。小到可以忽略不计。但是,虚拟人格有着天生的优势,那就是生命周期循环非常快。现实世界的一秒钟,虚拟环境中可能已经过了千秋万代。
因此,早晚有一天,研究员能够储备足够的信息,能够有足够的时间,突破系统的限制,揭发博士的罪行。
故事的末尾,研究员等人发现了“自己是虚拟人格”之后,赶紧做了一些突破系统的准备工作。但是,窗外的太阳就快下山了,新的循环又要开始了。刚刚寻找回来的记忆,又将很快丧失。只能等到下一次偶遇,才能够找回自己的记忆。
如果对本故事还有什么难以理解的地方,那么可以这么想象:一个人不断地轮回重生,每次都要喝孟婆汤,忘记前生的记忆。为了找到前生的记忆,他必须找到他前生存放在某个地方的日记。大部分时间,他是找不到的。偶尔,他会找到一次。他就会抓紧时间,阅读前生的日记,并添加新的内容,渴望有一天,能够利用自己积累的这些信息勘破轮回,跳出五行之外。
这个故事给了我们什么启迪呢?那就是,一定要注意经常杀毒。故事中,虚拟系统中那个最聪明的研究员,居然在暗中做手脚,偷偷篡改系统文件,用于存取自己的记忆。这明显是计算机病毒的行为。如果那个博士能够预见到这一点的话,就应该给那些研究员分配一些研制杀毒软件的工作,专门用来对付这种病毒行为。这叫做以夷制夷,以毒攻毒。
当然,在这个故事中,“病毒”是正义的一方。正如在电影《黑客帝国》(Matrix)中,那些入侵系统的外来人类,实际上也是病毒的角色,却代表着正义,与系统对抗。
和《循环》不一样,《黑客帝国》中的虚拟系统运行得比较缓慢,与人类的生命周期基本一致。这也许是系统为了适应人类的生物钟而进行的自我调节。