目录
2.1 进程的引入
2.1.1程序的顺序执行
1.程序的顺序执行
程序是人们要计算机完成特定功能的一些指令序列,是一个按严格次序、顺序执行的操作序列,是一个静态的概念。
如:有一个程序,要求先输入数据,再做相应的计算,最后输出结果并用打印机打印。分别用I、C、P代表以上3个程序段,这样,上述3个程序段的执行顺序为:
I→C→P。
2.程序顺序执行时的特征
(1)顺序性。
处理机的操作严格按照程序所规定的顺序执行,即只有前一个程序段完成才执行下一个程序段,上一条指令完成再去执行下一条指令。
(2)封闭性。
程序是在封闭环境下运行的。
程序运行时独占全机资源,资源的状态除初始状态外,只有该程序本身才能改变它。
程序执行的最终结果由给定的初始条件决定,不受外界因素的影响。
(3)可再现性。
顺序执行的最终结果可再现。
也就是说它与执行速度及执行的时刻无关,只要输入的初始条件相同,无论何时重复执行该程序,结果都是相同的。
2.1.2 程序的并发执行及其特征
1.并发执行的概念
所谓程序的并发性,是指多道程序在同一时间间隔内同时发生。
程序的并发执行可总结为:一组在逻辑上互相独立的程序或程序段在执行过程中,其执行时间在客观上互相重叠,即一个程序段的执行尚未结束,另一个程序段的执行已经开始的一种执行方式。
2.程序并发执行时的特征
(1)间断性
程序在并发执行时,由于它们共享系统资源,以及为完成同一项任务而相互合作,致使这些并发执行的程序之间,形成了相互制约的关系。相互制约将导致并发程序具有“执行——暂停——执行”这种间断性的活动规律。
(2)失去封闭性
程序在并发执行时,是多个程序共享系统中的各种资源,因而这些资源的状态将由多个运行的程序来改变,致使程序的运行失去了封闭性。例如,当处理机被某个程序占有时,另一程序必须等待。
(3)不可再现性
在并发环境下,同一个程序执行多次,执行的结果可能不同。
例如,有两个循环程序A和B,它们共享一个变量N,初值为0。
程序A(): 程序B():
{do {do
N=N+1; print(N);
While(1) While(1)
} }
程序A和程序B以不同的速度运行,出现的结果可能不同。例如,当程序A和程序B运行的速度相近时,打印的结果为1、2、3、…;而当程序A的执行速度是程序B的2倍时,打印的结果可能是2、4、6…。
小结:引入进程的意义
从上例来看,程序在并发执行时,由于失去了封闭性,其计算结果与并发程序的执行速度有关,从而使程序失去了可再现性,程序经过多次执行后,虽然执行时的环境和初始条件相同,但得到的结果却各不相同。
所以,从上面的讨论看,由于程序的顺序性、间断性和不可再现性,用程序作为描述其执行过程以及共享资源的基本单位是不合适的。
这就需要一个既能描述程序的执行过程,又能用来共享资源的基本单位,这个基本单位被称为进程。
2.1.3 进程的定义与特征
1.进程的定义
进程是操作系统中最基本、最重要的概念之一。人们对进程下过许多定义。现列举其中的几种:
(1)进程是程序的一次执行。
(2)进程是可以和别的进程并发执行的计算。
(3)进程就是一个程序在给定活动空间和初始条件下,在一个处理机上的执行过程。
(4)进程是程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位。
(5)进程是动态的,有生命周期的活动。内核可以创建一个进程,最终将由内核终止该进程使其消亡。
综上观点定义为:“并发执行的程序在一个数据集合上的执行过程” 【运行着的程序】
补充:进程和程序之间的关系
进程和程序是两个完全不同的概念,但又有密切的联系。程序是规定的工作流程,进程则是实际的工作过程。
它们之间的主要区别是:
(1)程序是静态的概念,而进程则是程序的一次执行过程。它是动态的概念。
(2)进程是一个能独立运行的单位,能与其它进程并发执行;而程序是不能作为一个独立运行的单位而并发执行的。
(3)程序和进程无一一对应的关系。
(4)各个进程在并发执行过程中会产生相互制约关系,而程序本身是静态的,不存在这种异步特征。
2.进程的特征
从进程与程序的区别可以看出,进程具有如下特征:
(1)动态性
动态性是进程最基本的特性。进程由创建而产生,由调度而执行,因得不到资源而暂停执行,以及因撤消而消亡。
(2)并发性
这是指多个进程实体,同存于内存中,能在一段时间段内同时执行。并发性是进程的重要特征,同时也是操作系统的重要特征。提高并发性,可以提高系统的效率。
(3)独立性
进程是一个能独立运行的基本单位,同时也是系统中独立获得资源和独立调度的基本单位。
(4)异步性
这是指进程按各自独立的、不可预知的速度向前推进;或者说,进程按异步方式运行。
(5)结构特征
从结构上看,进程实体是由程序段、数据段及进程控制块三部分组成,也称这三部分为进程映像。
2.1.4 进程的基本状态及转换
进程的动态性由它的状态及状态转换来体现的。
1.进程的三个基本状态
进程通常至少有三种基本状态:
(1)就绪状态(ready)
进程运行所需的外部条件满足,但因为其它进程已占用CPU,所以暂时不能运行。
(2)执行状态(running)
外部条件满足,进程已获得CPU,其程序正在执行。在单处理机系统中,只有一个进程处于执行状态。
(3)阻塞状态(blocked)
进程因资源无法满足而等待资源,暂时不能运行的状态,称为阻塞状态,也称为等待状态。
系统中处于阻塞状态的进程可能有多个,通常将它们排成一个队列,也有的系统则根据阻塞原因的不同将这些进程排成多个队列。
2.进程状态的转换
(1)就绪 ----> 执行
对于处于就绪状态的进程,在调度程序为之分配了处理机之后,该进程便可执行。相应地,它由就绪状态转变为执行状态。
(2)执行 ----> 就绪
正在执行的进程(执行状态)也称为当前进程,如果因分配给它的时间片已用完而被暂停执行时,该进程便由执行状态又回到就绪状态;
(3)执行 ----> 阻塞
一个处在执行状态的进程,如果因等待资源而使进程的执行受阻,使之无法继续执行,该进程将由执行状态转变为阻塞状态。
(4)阻塞 ----> 就绪
一个处于阻塞状态的进程,当它所需的外部事件满足,它应由阻塞状态变为就绪状态。
补充:引入挂起状态时的进程状态------挂起 、激活
除了上述3种基本状态以外,很多系统中又引入了挂起状态。
所谓挂起状态,实际上就是一种静止的状态。一个进程被挂起后,不管它是否在就绪状态,系统都不分配给它处理机。
处于挂起状态的进程要想重新进入到活动状态,必须被激活(即转换为活动状态),然后才有可能执行。
因此在引入挂起状态后,进程之间的状态转换除了四种基本状态转换以外,又增加了以下几种:
(1)活动就绪—挂起—静止就绪。
(2)活动阻塞—挂起—静止阻塞。
(3)静止就绪—激活—活动就绪。
(4)静止阻塞—激活—活动阻塞。
2.1.5 Linux进程的状态
Linux系统的一个任务总体上有以下几种状态:
(1)TASK-RUNNING状态 :执行和就绪两种状态
(2)TASK-INTERRUPTIBLE状态:可中断的等待状态。
(3)TASK-UNINTERRUPTIBLE状态:不可中断等待状态。
(4)TASK-ZOMBIE状态,僵死状态。由于某些原因进程被终止,这个进程所占有的资源全部释放之后,还保存着PCB信息,这种占有PCB但已被撤消的进程就处于僵死状态。
(5)TASK-STOPPED状态,暂停状态。
2.2 进程的描述
进程的活动是通过在CPU上执行一系列程序和对相应数据进行操作来体现的。
程序和操作的数据是进程存在的实体。
程序和数据是静态的,无法反映出其动态性,还需要一个数据结构来描述进程的动态性(当前的状态、本身的特性等)。这种数据结构称为进程控制块PCB (Process Control Block)。 所以,进程实体通常是由程序、数据集合和进程控制块PCB这三部分构成,也称为“进程映象”。
2.2.1 进程控制块PCB
PCB集中反映一个进程的动态特征,当系统创建了一个新进程时,就为它建立一个PCB;当进程终止后,系统回收其PCB,该进程在系统中就不存在了。所以,PCB是进程存在的惟一标志。 可以按照功能将PCB分成四个组成部分:
进程标识符、处理机状态、进程调度信息、进程控制信息。
1.进程标识符
进程标识符用于惟一地标识一个进程。
一个进程通常有两种标识符: 进程内部标识符、 进程外部标识符。
(1)进程内部标识符。
在所有的操作系统中,为每一个进程赋予一个惟一的数字标识符,它通常是一个进程的序号。设置内部标识符主要是为了方便系统使用。
(2)进程外部标识符。
它由创建者提供,通常是由字母、数字组成,往往由用户(进程)在访问该进程时使用。为了描述进程的家族关系,还应设置父进程标识及子进程标识。
2.处理机状态
处理机状态信息主要由处理机的各种寄存器中的内容组成。处理机在运行时,许多信息都放在寄存器中。当处理机被中断时,这些信息都必须保存在PCB中,以便在该进程重新执行时能从断点继续执行。处理机的寄存器包括通用寄存器、指令计数器、程序状态字PSW、用户栈指针。
3.进程调度信息
PCB中还存放一些与进程调度和进程对换有关的信息。
(1)进程状态。
指明进程当前的状态,作为进程调度和对换的依据。
(2)进程优先级。
进程使用处理机的优先级别的整数,优先级高的进程应优先获得CPU。
(3)进程调度所需要的其它信息。
与所采用的进程调度算法有关,如进程已等待CPU的时间、进程已运行的时间等。
(4)事件或阻塞原因。
指进程由执行状态转变为阻塞状态所需要等待发生的事件。
4.进程控制信息
进程控制信息包括:
(1)程序和数据的地址:
进程的程序和数据所在的内存或外存地址,以便再调度到该进程执行时,能从PCB中找到其程序和数据。
(2)进程同步和通信机制:
实现进程同步和进程通信时必需的机制,如消息队列指针、信号量等,它们可能全部或部分地放在PCB中。
(3)资源清单:
是一张列出了除CPU以外、进程所需的全部资源及已经分配到该进程的资源清单。
(4)链接指针:
本进程PCB所在队列的下一个进程PCB的首地址。
2.2.2 进程控制块的组织方式
各进程的PCB有如下几种组织方式:
线性方式、 链接方式、索引方式。
1.线性方式
将各进程的PCB依次放入一个表中,结构如下图所示。
2.链接方式
链接方式是经常采用的方式。其原理是:按照进程的不同状态分别将其放在不同的队列。Linux操作系统就是应用这种进程控制块组织方式。
3.索引方式
系统根据所有进程的状态建立几张索引表。如就绪索引表、阻塞索引表等。并把各索引表在内存的首地址记录在内存的一些专用单元中。 在每个索引表的表目中,记录具有相应状态的某个PCB在PCB表中 的地址。
2.2.3 Linux进程的PCB
Linux系统中的进程称为任务。
该系统的进程控制块PCB用一个称为task-struct的结构体来描述, Linux系统PCB包含以下信息:
1. 进程描述信息
(1)进程标识号(pid, process identifier)
(2)用户和组标识(user and group identifier)
(3) 连接信息(Links)
2.进程控制信息
(1) 进程当前状态
(2)调度信息
(3)记时信息
(4) 通信信息
3. 进程资源信息
记录了与该进程有关的存储器的各种地址和资料、文件系统以及打开文件的信息等等。
4. CPU现场信息
每个进程运行时都要使用处理器的寄存器以及堆栈等资源。当一个进程被挂起时,所有有关处理处理器的内容都要保存到task_struct结构中。当进程恢复运行时,所有保存的内容再装入到处理器中。
2.3 进程控制
进程和处理机管理的一个重要任务是进程控制。
所谓进程控制,就是系统使用具有特定功能的程序段来创建、撤消进程以及完成进程各状态间的转换,从而达到多进程高效率并发执行和协调、实现资源共享的目的。
系统在运行时分为两种状态:即核心态和用户态。
核心态也叫系统态或管态,是指CPU在运行操作系统的核心模块;用户态也称用态,是指CPU正在运行用户的程序。
原语的概念:把系统态下执行的某些具有特定功能的程序段称为原语,原语的特点是不可被中断。
系统在创建、撤消一个进程以及要改变进程的状态时,都要调用相应的程序段来完成这些功能。用于进程控制的原语有创建原语、撤消原语、阻塞原语和唤醒原语等。
2.3.1 进程的家族关系
操作系统通过内核原语来实现进程控制。在系统初始化完成后,系统就可创建进程。
创建者称为父进程,被创建的新进程称为子进程,子进程又可以创建自己的子进程,从而形成一棵有向的进程家族树。
子进程与父进程之间的关系:
子进程的许多属性都是从父进程继承来的,子进程与父进程的区别是形成自己独立的属性。 子进程可以从父进程继承的属性包括:用户标识符、环境变量、打开文件、文件系统的当前目录、已经连接的共享存储区和信号处理处理例程入口表等。
子进程不能从父进程继承的属性包括:进程标识符和父进程标识符等。
2.3.2 进程的创建与终止
1.进程的创建 fork.c
在多道程序环境下,为使程序运行,必须为他创建进程。
系统发现要求创建进程的事件请求后,便通过调用创建原语Creat( )创建进程。
其步骤如下:
( 1)申请空白PCB。
为新进程申请获得惟一的进程标识符,并从PCB集合中索取一个空白PCB。
(2)为新进程分配资源。
包括新创建进程的程序、数据及用户栈所需的内存空间。
(3)初始化进程控制块。
初始化标识信息,如进程标识符;处理机状态信息,使程序计数器指向程序的入口地址,使栈指针指向栈顶;处理机控制信息,将新建进程的状态设置为就绪状态(活动就绪或静止就绪状态);进程的优先级等。
(4)将新建进程插入就绪态队列。
2.进程的终止过程
系统检测到要求进程终止事件,操作系统调用进程终止原语,终止本进程的执行。操作过程如下:
(1)根据被终止进程的标识符,从PCB队列中检索出该进程的PCB,从中读出该进程的状态。 (2)若被终止进程正处于执行状态,应立即终止该进程的执行,该进程被终止后应重新进行进程调度。
(3)检查该进程有无子孙进程,若有,则应将其所有子孙进程终止。
(4)释放终止的进程所占有的资源,将其归还它的父进程或者系统。
(5)将被终止的进程从它的PCB队列中移出。
2.3.3 进程的阻塞与唤醒
进程状态的转换需要通过进程之间的同步或通信机构来实现,也可直接使用“阻塞原语”和“唤醒原语”来实现。
阻塞原语在一个进程期待某一事件发生,但发生条件还不满足时,被该进程自己调用来阻塞自己,并转换为等待状态。
当等待队列中的进程所等待的事件发生时,等待该事件的进程都将被唤醒。唤醒一个进程有两种方法,一种是由系统进程唤醒,另一种是由事件发生进程唤醒。
实现进程的执行状态到阻塞状态的原语为阻塞原语;由阻塞状态到就绪状态转换的原语分为唤醒原语 。
2.3.4 Linux系统调用
在Linux系统中,系统向用户提供了一些对进程进行控制的系统调用。常用的有:
1.fork( )系统调用 fork.c
Linux利用fork( ) 系统调用创建一个新进程。
fork()系统调用的格式是: int fork();
通常情况下,设返回值为int pid,调用格式为:pid=fork(); fork()是通过复制来创建子进程的,子进程继承父进程的上下文,是父进程的一个副本,与父进程使用同一段代码。在该系统调用之后,两个代码相同的进程并发执行。fork系统调出错可能基于以下两方面的原因:一是当前进程数量已达到系统规定的最大值,二是系统内存不足。
通常情况下,设返回值为int pid,调用格式为: pid=fork(); 其中,返回值pid意义如下: pid=0:创建子进程成功,表示从子进程返回,即CPU正在运行该子进程。 pid>0:创建子进程成功,表示从父进程返回,pid的值为新创建的子进程标识号。 pid=-1:创建失败。
用进程的家族关系
main()
{
fork();
fork();
fork();
printf(“S”);
}
结果:SSSSSSSS
2.Exec系统调用
利用exec系统调用执行另一个程序。
在Linux系统中,当由fork()系统调用创建一个子进程后,可再利用exec( )系统调用执行另一个程序。
3.exit( )系统调用
对于一般的用户进程,在其任务完成后应被尽快撤消。
Linux系统用exit()系统调用来实现进程的自我终止。通常,父进程在创建子进程时,应在进程的末尾写一条exit(),使子进程自我终止。
4.wait系统调用
将调用进程挂起,直至其子进程因暂停或终止而发来软中断信号为止。
2.4 进程的同步与互斥
进程的并发提高了系统效率,也同时导致了资源的竞争与共享。必须控制好进程的同步与互斥。
2.4.1 临界资源的概念
1.临界资源
两个或两个以上的进程不能同时使用的资源为临界资源。 临界资源可能是一些独占设备,如打印机、磁带机等;也可能是一些共享变量、表格、链表等。
2.临界区
不论硬件临界资源,还是软件临界资源,多个进程必须互斥地访问临界资源。
临界区: 每个进程中访问临界资源的那段代码称为临界区。
进入区: 在临界区前面增加一段用于进行检查是否正被其他进程访问的的代码,把这段代码称为进入区,进入区设置访问标志;
退出区: 在临界区后面再加一段用于退出临界区的代码,称为退出区,退出区取消访问标志。 剩余区: 进程中除去上述各区外,其它部分的代码,称为剩余区。
因此,一个访问临界资源的进程描述如下:
repeat
进入区:检查是否有进程使用临界资源,有则阻塞自己,无则设置标志
临界区:使用临界资源
退出区:使用完临界资源后释放临界资源,设置未使用标志,其他进程使用
剩余区:其他代码
until false;
2.4.2 进程的互斥与同步
1.同步与互斥的概念
进程互斥是指多个进程不能同时使用同一个临界资源CR,即两个或两个以上进程必须互斥地使用临界资源,或不能同时进入临界区CS。
两个逻辑上完全独立、毫无关系的进程,由于竞争同一个资源而相互制约,就称为进程的互斥。如系统只有一台打印机,两个进程都要使用。为了保证打印结果的正确和方便使用,只能一个进程用完打印机后,另一个进程才能使用。
进程同步,是指有协作关系的进程之间,要不断地调整它们之间的相对速度或执行过程,以保证临界资源的合理利用和进程的顺利执行。实现进程同步的机制称为进程同步机制。如两个进程合作使用同一个缓冲区。设进程A负责往缓冲区中输入数据,进程B负责从同一缓冲区中输出数据。当进程A将数据输满缓冲区,则只有当进程B将该数据读出后,进程A才能继续使用该缓冲区,否则将造成数据丢失。此时,进程A和进程B之间就形成了同步关系。
2.同步机制应遵循的规则
为实现进程互斥地进入自己的临界区,所有同步机制都应遵循下列准则:
(1)空闲让进:并发进程中某个进程不在临界区时,不阻止其他进程进入临界区。
(2)忙则等待:并发进程中的若干个进程申请进入临界区时,只允许一个进程进入。 当已有进程进入临界区时,其他申请进入临界区的进程必须等待,以保证对临界资源的互斥访问。
(3)有限等待:保证等待有限时间,不能无限期等待,陷入“等死”状态 。
(4)让权等待:当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态。
2.4. 3 实现进程同步的软件方法
1981年,G.L.Peterson提出了一个简单的算法来解决进程互斥进入临界区的问题。这种方法描述为:为每个进程设置一个标志,当标志值为true时,表示此进程要求进入临界区,另外,再设置一个指示器turn,即当turn==i时,表示进程pi可以进入临界区。 以下是Peterson算法的描述。
2.4.4 实现进程同步的硬件机制
许多计算机已经提供了一些特殊的硬件指令,允许对一个字中的内容进行检测和修改正,或者是对两个字的内容进行交换等。可利用这些特殊的指令来解决临界区问题。下面是实现对临界区管理的硬件方法。
1.关中断
实现互斥最简单的方法是在进程进入临界区时关中断,进程退出临界区时开中断。中断被关后,时钟中断也被屏蔽,进程上下文切换都是由中断事件引起的,这样进程的执行就不会被打断,因此采用关中断、开中断的方法就可确保并发进程互斥地进入临界区。
2.测试并设置指令
使用硬件所提供的“测试并设置”机器指令TS(Test and Set),实现进程之间的互斥。该指令是一条原语,需独立执行,可把这条指令看作函数,它的返回值和参数都是布尔类型。当TS(&x)测到x值为true时,置x=false,且根据所测试到的x值形成条件码。
3.对换指令
对换指令swap用于交换两个字的内容。
2.5 信号量机制
2.5.1 信号量的概念
信号量(Semaphore),也叫做信号灯,它是在信号量同步机制中用于实现进程的同步和互斥的有效数据结构。
可以为每类资源设置一个信号量。
它有多种类型的数据结构,即:整型信号量、记录型信号量、AND型信号量及信号量集等。 申请和释放临界资源的两个原语操作:
P操作--wait操作; 进入区
V操作--signal操作; 退出区
P、V操作的操作对象是信号量
1.整型信号量
整型信号量的数值表示当前系统中可用的该类临界资源的数量。
如设置整型信号量s,则s的值意义为:
s > 0,则s的值表示系统中空闲的该类临界资源的个数;
s = 0,则表示系统中该类临界资源刚好全部被占用,而且没有进程在等待该临界资源;
s < 0,则s的绝对值表示系统中的进程等待该类临界资源的个数;
2.记录型信号量
记录型信号量的数据结构由两部分构成。定义记录型信号量类型,有如下描述:
struct semaphore
{
int s;
struct PCB *queue;
}semaphore;
定义记录型信号量S,则:s的值value表示系统中可用的该类临界资源的数量; queue为进程链表指针,指向等待该类资源的PCB队列
2.5.2 信号量的申请与释放
1、p操作----释放资源
2、v操作 ----申请资源
2.5.3 利用信号量实现进程的同步和互斥
可以用wait(s)和signal(s)操作处理飞机买票问题。 对临界资源n设一互斥信号量S,将原进程T1及T2做如下修改:
2.6 进程同步问题举例
利用P、V操作可以实现进程之间的同步。
关于这类问题的应用有两种类型: 一种是对于临界资源,在使用之前申请,使用之后释放,我们给这类资源设一个互斥信号量即可;
第二种类型是利用信号量控制进程之间运行的顺序,这时需要根据实际情况设置多于一个信号量,对同一个信号量的P、V操作在不同的进程之间进行。
2.6.1 两个简单的例子
例1
系统中有多个进程,共同使用一台打印机。写出这些进程并发执行时,同步使用打印机的程序段。
例2
有一个缓冲区,供多个进程共享。这些进程中有生产者进程和消费者进程。写出多个进程共同使用同一个缓冲区时实现进程同步的程序。
1.问题的描述
有一批生产者进程在生产产品,并将这些产品提供给消费者进程去消费。
生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池.
生产者进程将它所生产的产品放入一个缓冲区中;
消费者进程可从一个缓冲区中取走产品去消费。
规定消费者进程不能到一个空缓冲区中去取产品;
生产者进程不能将产品放入一个已装满产品且尚未被取走的缓冲区中。
2.在生产者—消费者问题中应注意:
(1)在每个程序中用于实现互斥的P(mutex)和V(mutex)必须成对地出现。
(2)对资源信号量empty和full的P和V操作,同样需要成对地出现,但它们分别处于不同的进程中,这样保证生产者进程和消费者进程的同步及交替执行。
(3)在每个进程中,两个P操作顺序不能颠倒,而signal操作的次序是无关紧要的 。