第2章 进程与线程

从本章开始我们将深入考察操作系统是如何设计和构造的。操作系统中最核心的概念是进程:这是对正在运行程序的一个抽象。操作系统的其他所有内容都是围绕着进程的概念展开的,所以,让操作系统的设计者(及学生)尽早并透彻地理解进程是非常重要的。

进程是操作系统提供的最古老的也是最重要的抽象概念之一。即使可以利用的CPU只有一个,但它们也支持(伪)并发操作的能力。它们将一个单独的CPU变换成多个虚拟的CPU。没有进程的抽象,现代计算将不复存在。在本章里我们会通过大量的细节去探究进程,以及它们的第一个亲戚——线程。

2.1 进程

所有现代的计算机经常会在同一时间做许多件事。习惯于在个人计算机上工作的人们也许不会十分注意这个事实,因此列举一些例子可以更清楚地说明这一问题。先考虑一个网络服务器。从各处进入一些网页请求。当一个请求进入时,服务器检查是否其需要的网页在缓存中。如果是,则把网页发送回去;如果不是,则启动一个磁盘请求以获取网页。然而,从CPU的角度来看,磁盘请求需要漫长的时间。当等待磁盘请求完成时,其他更多的请求将会进入。如果有多个磁盘存在,会在满足第一个请求之前就接二连三地对其他的磁盘发出一些或所有的请求。很明显,需要一些方法去模拟并控制这种并发。进程(特别是线程)在这里就可以产生作用。

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元

现在考虑只有一个用户的PC。一般用户不知道,当启动系统时,会秘密启动许多进程。例如,启动一个进程用来等待进入的电子邮件;或者启动另一个防病毒进程周期性地检查是否有新的有效的病毒定义。另外,某个用户进程也许会在所有用户上网的时候打印文件以及烧录CD-ROM。所有的这些活动需要管理,于是一个支持多进程的多道程序系统在这里就显得很有用了。

在任何多道程序设计系统中,CPU由一个进程快速切换至另一个进程,使每个进程各运行几十或几百个毫秒。严格地说,在某一个瞬间,CPU只能运行一个进程。但在1秒钟期间,它可能运行多个进程,这样就产生并行的错觉。有时人们所说的伪并行就是指这种情形,以此来区分多处理器系统(该系统有两个或多个CPU共享同一个物理内存)的真正硬件并行。人们很难对多个并行活动进行跟踪。因此,经过多年的努力,操作系统的设计者发展了用于描述并行的一种概念模型(顺序进程),使得并行更容易处理。有关该模型、它的使用以及它的影响正是本章的主题。

2.1.1 进程模型

在进程模型中,计算机上所有可运行的软件,通常也包括操作系统,被组织成若干顺序进程(sequential process),简称进程(process)。一个进程就是一个正在执行程序的实例,包括程序计数器、寄存器和变量的当前值。从概念上说,每个进程拥有它自己的虚拟CPU。当然,实际上真正的CPU在各进程之间来回切换。但为了理解这种系统,考虑在(伪)并行情况下运行的进程集,要比我们试图跟踪CPU如何在程序间来回切换简单得多。正如在第1章所看到的,这种快速的切换称作多道程序设计。

在图2-1a中我们看到,在一台多道程序计算机的内存中有4道程序。在图2-1b中,这4道程序被抽象为4个各自拥有自己控制流程(即每个程序自己的逻辑程序计数器)的进程,并且每个程序都独立地运行。当然,实际上只有一个物理程序计数器,所以在每个程序运行时,它的逻辑程序计数器被装入实际的程序计数器中。当该程序执行结束(或暂停执行)时,物理程序计数器被保存在内存中该进程的逻辑程序计数器中。在图2-1c中我们看到,在观察足够长的一段时间后,所有的进程都运行了,但在任何一个给定的瞬间仅有一个进程真正在运行。

阅读 ‧ 电子书库
图 2-1 a)含有4道程序的多道程序;b)4个独立的顺序进程的概念模型;c)在任意时刻仅有一个程序是活跃的

在本章,我们假设只有一个CPU。然而,逐渐这个假设就不为真了,因为新的芯片经常是多核的,包含2个、4个或更多的CPU。我们将会在第8章介绍多核芯片以及多处理器,但是在现在,一次只考虑一个CPU会更简单一些。因此,当我们说一个CPU只能真正一次运行一个进程的时候,即使有2个核(或CPU),每一个核也只能一次运行一个进程。

由于CPU在各进程之间来回快速切换,所以每个进程执行其运算的速度是不确定的。而且当同一进程再次运行时,其运算速度通常也不可再现。所以,在对进程编程时决不能对时序做任何确定的假设。例如,考虑一个I/O进程,它用流式磁带机恢复备份的文件,它执行一个10 000次的空循环以等待磁带机达到正常速度,然后发出命令读取第一个记录。如果CPU决定在空循环期间切换到其他进程,则磁带机进程可能在第一条记录通过磁头之后还未被再次运行。当一个进程具有此类严格的实时要求时,也就是一些特定事件一定要在所指定的若干毫秒内发生,那么必须采取特殊措施以保证它们一定在这段时间中发生。然而,通常大多数进程并不受CPU多道程序设计或其他进程相对速度的影响。

进程和程序间的区别是很微妙的,但非常重要。用一个比喻可以使我们更容易理解这一点。想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕。他有做生日蛋糕的食谱,厨房里有所需的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序(即用适当形式描述的算法),计算机科学家就是处理器(CPU),而做蛋糕的各种原料就是输入数据。进程就是厨师阅读食谱、取来各种原料以及烘制蛋糕等一系列动作的总和。

现在假设计算机科学家的儿子哭着跑了进来,说他的头被一只蜜蜂螫了。计算机科学家就记录下他照着食谱做到哪儿了(保存进程的当前状态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这里,我们看到处理机从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程拥有各自的程序(食谱和急救手册)。当蜜蜂螫伤处理完之后,这位计算机科学家又回来做蛋糕,从他离开时的那一步继续做下去。

这里的关键思想是:一个进程是某种类型的一个活动,它有程序、输入、输出以及状态。单个处理器可以被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。