第二章进程
2.1 进程介绍
2.1.1 进程模型
进程是对正在运行程序的一个抽象,包括程序计数器(PC)、寄存器和变量的当前值,从概念上说,每个进程都拥有他自己的虚拟CPU,实际上真正的CPU在各进程之间来回切换。
进程和程序的类比:想象一位有一手好厨艺的计算机科学家正在为他的女儿烘制生日蛋糕。他有制作生日蛋糕的食谱,厨房里有所需的原料:面粉、鸡蛋、糖、香草汁等。在这个比喻中,做蛋糕的食谱就是程序(即用适当形式描述的算法),计算机科学家就是处理器(CPU),而做蛋糕的各种原料就是输人数据。进程就是厨师阅读食谱、取来各种原料以及烘制蛋糕的一系列动作的总和。
现在假设计算机科学家的儿子哭着跑了进来,说他被一只蜜蜂蛰了。计算机科学家就记录下自己照着食谱做到哪儿了(保存进程的当前状态),然后拿出一本急救手册,按照其中的指示处理蛰伤。这里可看到处理器从一个进程(做蛋糕)切换到另一个高优先级的进程(实施医疗救治),每个进程拥有各自的程序(食谱和急教手册)当蜜蜂整伤处理完之后,计算机科学家又回来做蛋糕从他离开时的那一步继缝做下去。
这里的关键思想就是:一个进程是某种类型的一个活动,它有程序、输入、输出及状态。单个CPU被若干进程共享,它使用某种调度算法决定何时停止一个进程的工作,并转而为另一个进程提供服务。
2.1.2 进程创建
在通用系统中,需要某种方法在运行时按照需要来创建和终止进程。
进程的创建有四个主要原因
系统初始化 |
正在运行的一个进程执行了创建进程的系统调用 |
用户请求创建一个新进程 |
批处理作业的初始化 |
启动操作系统时,通常会创建若干个操作系统:其中一些是前台进程(与用户交互并替他们完成工作的进程);另外一些是后台进程,不与特定的用户进行联系,但是具有某些特定的功能(例如用来接收访问这台机器上网页的请求信息等,在请求信息到来后就会醒来并处理这个请求),这些后台用来处理网页、打印之类活动的进程称为守护进程(daemon);
通常一个正在运行的进程会发出系统调用创建一个或者多个进程来协助其完成工作;
在交互式系统中,用户可以通过输入命令来启动程序;
最后一种情形仅在大型机的批处理系统中应用,用户在这种系统中(可能是远程地)提交批处理作业。在操作系统认为有资源运行另一个作业时,它创建一个新的进程,并运行其输人队列中的一个作业。
从技术上看,新进程都是由一个已经存在的进程执行了进程创建的系统调用(fork)而创建的,这个进程可以是一个运行的用户进程、一个键盘或者鼠标启动的系统进程或一个批处理管理进程。
在调用fork成功后,父子进程拥有相同的内存映像、相同的环境字符串和相同的打开文件,通常子进程执行exec来修改其内存映像,并运行一个新的程序;之所以要安排两个步骤来创建进程,是为了在fork和exec命令之间,完成对标准输入文件、标准输出文件和标准输出错文件等文件描述符操作。
进程创建完成之后,父子进程拥有各自独立的地址空间(子进程的初始地址空间是父进程的一个副本)。可写的地址空间是不共享的(如果某个子进程在其地址空间修改了一个字,则这一改变对另一个进程是不可见的);不可写的地址空间是共享的(父子进程之间共享代码段)。
2.1.3进程的终止
正常退出(自愿) |
出错退出(自愿) |
严重错误(非自愿) |
被其他进程杀死(非自愿) |
多数进程由于完成了他们的工作而终止;
进程发现了严重的错误,例如,用户键入命令:
cc foo.c
来编译程序,但是这个文件不存在,那么编译器就会简单退出。
进程引起的错误,通常是由于程序中的错误导致的,例如一条非法指令,引用了不存在的内存,除数为0;
某个进程执行了系统调用(kill)通知系统杀死某个进程。
2.1.4 进程的层次结构
进程只有一个父进程,但可以有0/1或者多个父进程。
2.1.5 进程的状态
1.运行态:在该时刻实际占用CPU;
2.就绪态:可运行,因为其它进程正在运行而暂时被挂起;
3.阻塞态:除非某种外部事件发生,否则不能运行;
阻塞态的理解:如下shell命令
cat chaprer1 chapter2 chapter3 | grep tree
中,第一个进程运行cat,将三个文件链接并输出,第二个进程运行grep,从输入中选择所有包含tree的那些行。根据这两个进程的相对速度(具体取决于这两个进程的相对复杂度和各自分配到的CPU时间),grep可能已经做好了运行的准备,但还没有输入,于是就必须被阻塞直到有输入。
当一个进程在逻辑上不能运行时,就会被阻塞。典型的例子就是它在等待输入(自身引起的);另一个可能的情况是:逻辑上能够运行的进程由于操作系统调度另一个进程占用CPU而被迫阻塞(没有足够的CPU,不能使每个进程都有其一台私有的CPU)。
三种状态由四种转换关系:
当进程发现自己无法继续运行运行时则发生转换1,如当一个进程从管道或特殊文件(例如终端)读取数据时,如果没有可用的输人,则进程自动从运行态转换为阻塞态。
进程调度器决定转换关系2和3,进程调度器是操作系统的一部分,如果调度器认为运行的进程占用CPU的时间已经足够长,决定让其它进程占用CPU时,发生转换关系2;在系统已让其他进程享有了它们应有的CPU时间而重新轮到该进程来占用处理机时,发生转换3;调度器的主要内容是决定哪个进程应当运行,以及它应运行多长时间。
当一个进程等待的一个外部事件发生时(例如输入到达),发生转换4。如果此时设有其他进程运行,则立即触发转换3,该进程便开始运行。否则该进程将处于就绪态,等待调度暴把CPU分配给它。
2.1.6 进程的实现
为了实现进程模型,操作系统维护者着一张表格(一个结构化数组),即进程表/进程控制块,每个进程占用了一个进程表项。该表项包含了PC、栈指针、内存分配情况、文件打开状态、统计和调度信息、定时器和其它信号,以及进程由运行态切换到进程态是所必须保存的其他信息。这样,进程随后能够被再次被启动,就行从未发生过停止 。
与每个I/O设备类(例如,软盘,硬盘,定时器,终端)相关联的是中断描述符表中的一个数据结构,表中最重要的一个实体被称为中断向量,它包含终端服务过程的地址。
假设一个磁盘中断发生时,进程23正在运行,则中断硬件将PC、程序状态字、以及一个或多个寄存器压入当前堆栈。从这里开始,软件开始接管操作。
中断服务过程的工作从把当前进程全部寄存器值存入进程表项开始。当前进程号及一个指向其表项的指针被保存在全局变量中,以便能够快速地找到它们。随后,将中断存人的那部分信息从堆栈中删除,并将栈指针指向一个被进程处理程序(processhander)所使用的临时堆栈。
进程间通信通过消息完成,所以下一步是构造一条发送给磁盘通知其发生了一条中断,这时磁盘正被阻塞并等待该消息。不同的进程有不同的优先级,如果当前磁盘进程具有高优先级,则它被调度运行。