2.1.1 进程的概念和特征
1. 进程的概念
在多道程序环境下,允许多个程序并发执行,此时它们将失去封闭性,并具有间断性及不可再现的特征。为此引入进程的概念,以便更好地描述和控制程序的并发执行,实现操作系统的并发性和共享性。
为了使参与并发执行的程序(含数据)能独立运行,必须为之配置一个专门的数据结构,称为进程控制块(Process Control Block,PCB)。系统利用PCB来描述进程的基本情况和运行状态,进而控制和管理进程。相应地,由程序段、相关数据和PCB三部分构成了进程映像(进程实体)。所谓创建进程,实质是是创建进程映像中的PCB;而撤销进程,实质上是撤销进程的PCB。进程映像是静态的,进程则是动态的。
比较典型的进程的定义:
1)进程是程序的一次执行过程。
2)进程是一个程序及其数据在处理机顺序执行时所发生的活动。
3)进程是具有独立功能的程序在一个数据集合上的运行过程,它是系统进行资源分配和调度的一个独立单位。
2. 进程的特征
1)动态性。动态性是进程最基本的特征。
2)并发性。引入进程的目的就是为了使程序能与其他进程的程序并发执行,以提高资源利用率。
3)独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。
4)异步性。异步性会导致执行结果的不可再现性,为此,在操作系统中必须配置相应的进程同步机制。
5)结构性。每个进程都配置一个PCB对其进行描述。从结构上看,进程实体是由程序段、数据段和进程控制段三部分组成。
2.1.2 进程的状态与转换
1)运行状态:进程正在处理机上运行。在单处理机环境下,每一时刻最多只有一个进程处于运行状态。
2)就绪状态:进程已处于准备运行的状态,即进程获得了除处理机之外的一切所需资源,一旦得到处理机即可运行。
3)阻塞状态,又称等待状态:进程正在等待某一事件而暂停运行,如等待某资源为可用或等待输入/输出完成。即使处理机空闲,该进程也不能运行。
4)创建状态:进程正在被创建,尚未转到就绪状态。创建进程通常需要多个步骤:首先申请一个空白的PCB,并向PCB中填写一些控制和管理进程的信息;然后由系统为该进程分配运行时所必需的资源;最后把该进程转入到就绪状态。
5)结束状态:进程正从系统中消失,这可能是进程正常结束或其他原因中断退出运行。当进程需要结束运行时,系统首先必需置该进程为结束状态,然后再进一步处理资源释放和回收等工作。
就绪状态是指进程仅缺少处理机,只要获得处理机资源就立即执行;而等待状态是指进程需要其他资源(除了处理机)或等待某一事件。
就绪状态→运行状态:处于就绪状态的进程被调度后,获得处理机资源(分派处理机时间片),于是进程由就绪状态转换为运行状态。
运行状态→就绪状态:处于运行状态的进程在时间片用完后,不得不让出处理机,从而进程由运行状态转换为就绪状态。此外,在可剥夺的操作系统中,当有更高优先级的进程就绪时,调度程序将正执行的进程转换为就绪态,让更高优先级的进程执行。
运行状态→阻塞状态:当进程请求某一资源的使用和分配或等待某一事件的发生时,它就从运行状态转换为阻塞状态。进程以系统调用的形式请求操作系统提供服务,这是一种特殊的、由运行用户态程序调用操作系统内核过程的形式。
阻塞状态→就绪状态:当进程等待的事件到来时,如I/O操作结束或中断结束时,中断处理程序必须把相应进程的状态由阻塞状态转为就绪状态。
一个进程从运行状态变为阻塞状态是一个主动的行为,而从阻塞状态变到就绪状态是一个被动的行为,需要其他相关进程的协助。
2.1.3 进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。在操作系统中,一般把进程控制用的程序段称为原语,原语的特点是执行期间不允许中断,它是一个不可分割的基本单位。
1.进程的创建
允许一个进程创建另一个进程。此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程那里获得的资源归还给父进程。此外,在撤销父进程时,也必须同时撤销其所有的子进程。
操作系统创建一个新进程的过程如下(创建原语):
1)为新进程分配一个唯一的进程标识号,并申请一个空白的PCB。若PCB申请失败则创建失败。
2)为进程分配资源,为新进程的程序和数据,以及用户栈分配必要的内存空间(在PCB中体现)。注意:这里如果资源不足,并不是创建失败,而是出于“等待状态”,或称为“阻塞状态”。
3)初始化PCB,主要包括初始化标志信息、初始化处理机状态信息和初始化处理机控制信息,以及设置进程的优先级。
4)如果进程就绪队列能够接纳新进程,就将新进程插入到就绪队列,等待被调度运行。
2. 进程的终止
引起进程终止的事件主要有:正常结束,表示进程的任务已经完成和准备退出运行。异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、I/O故障等。外界干预是指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。
操作系统终止进程的过程如下(撤销原语):
1)根据被终止进程的标识符,检索PCB,从中读出该进程的状态。
2)若被终止进程处于执行状态,立即终止该进程的执行,将处理机资源分配给其他进程。
3)若该进程还有子进程,则应将其所有子进程终止。
4)将该进程所拥有的全部资源,或归还给其父进程或归还给操作系统。
5)将该PCB从所在队列(链表)中删除。
3.进程的阻塞和唤醒
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作可做等,则由系统自动执行阻塞原语(Block),使自己由运行状态变为阻塞状态。可见,进程的阻塞是进程自身的一种主动行为,也因此只有处于运行态的进程(获得CPU),才能将其转为阻塞状态。阻塞原语的执行过程是:
1)找到将要被阻塞进程的标识号对应的PCB。
2)若该进程为运行状态,则保护其现场,将其状态转为阻塞状态,停止运行。
3)把该PCB插入到相应事件的等待队列中去。
当被阻塞进程所期待的事件出现时,则由有关进程调用唤醒原语(Wakeup),将等待该事件的进程唤醒。唤醒原语的执行过程是:
1)在该事件的等待队列中找到相应进程的PCB。
2)将其从等待队列中移出,并置其状态为就绪状态。
3)把该PCB插入就绪队列中,等待调度程序调度。
Block原语和Wakeup原语是一对作用刚好相反的原语,必须成对使用。Block原语是由被阻塞进程自我调用实现的,而Wakeup原语则是由一个与被唤醒进程相合作或被其他相关的进程调度实现的。
4.进程切换
进程切换是指处理机从一个进程的运行转到另一个进程上运行的,这个过程中,进程的运行环境产生了实质性的变化。进程切换的过程如下:
1)保存处理机上下文,包括程序计数器和其他寄存器。
2)更新PCB信息。
3)把进程的PCB移入相应的队列,如就绪、在某事件阻塞等队列。
4)选择另一个进程执行,并更新其PCB。
5)更新内存管理的数据结构。
6)恢复处理机上下文。
进程切换与处理机模式切换是不同的,模式切换时,处理机逻辑上可能还在同一进程中运行。如果进程因中断或异常进入到核心态运行,执行完后又回到用户态刚被中断的程序运行,则操作系统只需要恢复进程进入内核时所保存的CPU现场,无需改变当前进程的环境信息。但若切换进程,当前运行进程改变了,则当前进程的环境信息也需要改变。
2.1.4 进程的组织
进程是操作系统的资源分配和独立运行的基本单位。她一般由以下三个部分组成:
1. 进程控制块
进程创建时,操作系统就新建一个PCB结构,它之后就常驻内存,任一时刻可以存取,在进程结束时删除。PCB是进程实体的一部分,是进程存在的唯一标志。
PCB主要包括进程描述信息、进程控制和管理信息、资源分配清单和处理机相关信息等。各部分主要说明如下:
1)进程描述信息
进程标识符:标志各个进程,每个进程都有一个并且是唯一的标识符。
用户标识符:进程归属的用户,用户标识符主要为共享和保护服务。
2)进程控制和管理信息
进程当前状态:描述进程的状态信息,作为处理机分配调度的依据。
进程优先级:描述进程抢占处理机的优先级,优先级高的进程可以优先获得处理机。
3)资源分配清单,用于说明有关地址空间或虚拟地址空间的状态;所打开文件的列表和所使用的输入/输出设备信息。
4)处理机相关信息,主要指处理机中各寄存器值,当进程被切断时,处理机状态信息都必须保存在相应的PCB中,以便在该进程重新执行时,能从断点继续执行。
在一个系统中,通常存在着许多进程,有的处于就绪状态,有的处于阻塞状态,而且阻塞的原因各部相同。为了方便进程的调度和管理,需要将各进程的 PCB用适当的方法组织起来。目前,常用的组织方式有链接方式和索引方式两种。链接方式将同一状态的PCB链接成一个队列,不同状态对应不同的队列,也可以把处于阻塞状态的进程的PCB,根据其阻塞原因的不同,排成多个阻塞队列。索引方式是将同一状态的进程组织在一个索引表中,索引表的表项指向相应的PCB,不同状态对应不同的索引表,如就绪索引表和阻塞索引表等。
2. 程序段
程序段就是能被进程调度程序调度到CPU执行的程序代码段。程序可以被多个进程共享,就是说多个进程可以运行同一个程序。
3. 数据段
一个进程的数据段,可以是进程对应的程序加工处理的原始数据,也可以是程序执行时产生的中间或最终结果。
2.1.5 进程的通信
进程通信是指进程之间的信息交换。PV操作是低级通信方式,高级通信方式是指以较高效率传输大量数据的通信方式。高级通信方法主要有一下三个类。
1. 共享存储
在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行写/读操作实现进程之间的信息交换。在对共享空间进行写/读操作时,需要使用同步互斥工具(如P操作、V操作),对共享空间的写/读进行控制。共享存储又分为两种:低级方式的共享是基于数据结构的共享;高级方式则是基于存储区的共享。操作系统只负责为通信进程提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排读/指令完成。
用户进程空间一般都是独立的,进程运行期间一般不能访问其他进程的空间,要想让两个用户进程共享空间必须通过特殊的系统调用实现,而进程内的线程是自然共享进程空间的。
2. 消息传递
在消息传递系统中,进程间的数据交换是以格式化的消息为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。
1)直接通信方式:发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。
2)间接通信方式:发送进程把消息发送到某个中间实体中,接收进程从中间实体中取得消息。这种中间实体一般称为信箱,这种通信方式又称为信箱通信方式。该通信方式广泛用于计算机网络中,相应的通信系统称为电子邮件系统。
3. 管道通信
管道通信是消息传递的一种特殊方式。所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程),则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。
从管道读取数据是一次性操作,数据一旦被读取,它就从管道中被抛弃,释放空间以便写更多的数据。管道只能采取半双工通信,即某一时刻只能单向传输。
2.1.6 线程概念和多线程模型
1. 线程的基本概念
引入进程的目的,是为了更好地使用多道程序并发执行,以提高资源利用率和系统吞吐量,增加并发成都;而引入线程,则是为了减小程序在并发执行时所付出的时空开销,提高操作系统的并发性能。
线程最直接的理解就是“轻量级进程”,它是一个基本的CPU执行单元,也是程序执行流的最小单元,由线程ID、程序计数器、寄存器集合和堆栈组成。线程是进程中的一个实体,是被系统独立调度和分配的基本单位,线程自己不拥有系统资源,只拥有一点在运行中必不可少的资源,但它可与同属一个进程的其他线程共享进程所拥有的全部资源。一个线程可以创建和撤销另一个线程,同一进程中的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。线程也有就绪、阻塞和运行三种基本状态。
2. 线程与进程的比较
1)调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是拥有资源的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换,如从一个进程内的线程切换到另一个进程内的线程时,会引起进程切换。
2)拥有资源。不论是传统操作系统还是设有线程的操作系统,进程都是拥有资源的基本单位,而线程不拥有系统资源(也有一点必不可少的资源),但线程可以访问其隶属进程的系统资源。
3)并发性。不仅进程之间可以并发执行,而且多个线程之间也可以并发执行,从而使操作系统具有更好的并发性,提高了系统的吞吐量。
4)系统开销。
5)地址空间和其他资源。进程的地址空间之间相互独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
6)通信方面,进程间通信(IPC)需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接读/写进程数据段来进行通信。
3. 线程的属性
在多线程操作系统中,把线程作为独立运行(或调度)的基本单位。线程的主要属性如下:
1)线程是一个轻型实体,它不拥有系统资源,但每个线程都应有一个唯一的标识符和一个线程控制块,线程控制块记录了线程执行的寄存器和栈等现场状态。
2)不同的线程可以执行相同的程序,即同一个服务程序被不同的用户调用时,操作系统为它们创建成不同的线程。
3)同一进程中的各个线程共享该进程所拥有的资源。
4)线程是处理机的独立调度单位,多个线程是可以并发执行的。在单CPU的计算机系统中,各线程可交替地占用CPU;在多CPU的计算机系统中,各线程可同时占用不同的CPU,若各个CPU同时为一个进程内的各线程服务则可缩短进程的处理时间。
5)一个线程被创建后便开始了它的生命周期,直至终止,线程在生命周期内会经历阻塞态、就绪态和运行态等各种状态变化。
4. 线程的实现方式
线程的实现可以分为两类:用户级线程(ULT)和内核级线程(KLT)。内核级线程又称为内核支持的线程。
在用户级线程中,有关线程管理的所有工作都由应用程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程起始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。
在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也是在内核基于线程框架的基础上完成。
在一些系统中,使用组合方式的多线程实现。线程创建完全在用户空间中完成,线程的调度和同步也在应用程序中进行。一个应用程序中的多个用户线程被映射到一些(小于或等于用户级线程的数目)内核级线程上。
5. 多线程模型
有些系统同时支持用户线程和内核线程,由此产生了不同的多线程模型,即实现用户级线程和内核级线程的连接方式。
1)多对一模型。将多个用户线程映射到一个内核级线程,线程管理在用户空间完成。此模式中,用户级线程对操作系统不可见。
优点:线程管理是在用户空间进行的,因而效率比较高。
缺点:当一个线程在使用内核服务时被阻塞,那么整个进程都会被阻塞;多个线程不能并行地运行在多处理机上。
2)一对一模型。将每个用户线程映射到一个内核级线程。
优点:当一个线程被阻塞后,允许另一个线程继续执行,所以并发能力较强。
缺点:每创建一个用户级线程都需要创建一个内核级线程与其对应,这样创建线程的开销比较大,会影响到应用程序的性能。
3)多对多模型。将n个用户级线程映射到m个内核级线程上,要求m小于等于n。
特点:在多对一模型和一对一模型中取了折中,克服了多对一模型的并发度不高的缺点,又克服了一对一模型的一个用户进程占用太多内核级线程,开销太大的缺点。又拥有多对一模型和一对一模型各自的优点。