【操作系统】 进程管理
进程与线程
进程映像(进程实体)由程序段、相关数据和 PCB (进程控制块) 三部分构成。所谓创建进程,实质上是创建进程映像中的 PCB;而撤销进程,实质上是撤销进程的 PCB。值得注意的是进程映像是静态的,进程则是动态的。
PCB 是进程存在的唯一标志。
进程的基本特征:
- 动态性。进程是程序的一次执行,它有着创建、活动,暂停、终止等过程,具有一定的生命周期。是动态地产生、变化、和消亡的。动态性是进程最基本的特征。
- 并发性。是指多个进程实体同时存在于内存中,能在同一段时间内运行。
- 独立性。指进程实体是一个能独立运行、独立获得资源和独立接受调度的基本单位。
- 异步性。由于进程的相互制约,使得进程具有执行的间断性,即进程按各自独立的、不可预知的速度向前推进。异步性会导致结果的不不可再现性,为此在操作系统中必须配置相应的进程同步机制。
- 结构性,每个进程都配置一个 PCB 对其进行描述。从结构上看,进程是由程序段、数据段和进程控制段三部分组成的。
进程的状态与转换
一个进程从运行态变成阻塞态是主动的行为,而从阻塞态变成就绪态是被动的行为,需要其他 相关进程的协助。
允许一个进程创建另一个进程。此时创建者称为父进程,被创建的进程称为子进程。子进程可以继承父进程所拥有的资源。当子进程被撤销时,应将其从父进程哪里获得的资源归还给父进程。此外,在撤销父进程时,必须同时撤销其所有的子进程。
引起进程终止的事件主要有:正常结束,表示进程的任务已经完成并准备退出运行。异常结束,表示进程在运行时,发生了某种异常事件,使程序无法继续运行,如存储区越界、保护错、非法指令、特权指令错、I/O 故障等。外界干预是指进程应外界的请求而终止运行,如操作员或操作系统干预、父进程请求和父进程终止。
正在执行的进程,由于期待的某些事件未发生,如请求系统资源失败、等待某种操作的完成、新数据尚未到达或无新工作可做等,由系统自动执行阻塞原语(Block),使自己由运行态变为阻塞态。可见,进程的阻塞时进程自身的一种主动行为,也因此只有处于运行态的进程(获得 CPU )。才可能将其转为阻塞态。 当被阻塞进程所期待的事件出现时,如它所启动的 I/O 操作已完成或其所期待的数据已到达,由有关进程(如提供数据的进程)调用唤醒原语(Wakeup),将等待该事件的进程唤醒。 Block 原语和 Wakeup 原语是一对作用刚好相反的原语,必须成对使用。
对于通常的进程而言,其创建、撤销及要求由系统设备完成的 I/O 操作,都是利用系统调用而进入内核,再由内核中的相应处理程序予以完成的。进程切换同样是在内核的支持下实现的,因此可以说,任何进程都是在操作系统的支持下运行的,是与内核紧密相关的。 进程切换是指处理及从一个进程的运行转到另一个进程上运行。
调度是指决定资源分配给哪个进程的行为,是一种决策行为;切换是指实际分配的行为,是执行行为,一般来说,现有资源的调度,然后才有进程的切换。
进程通信是指进程之间的信息交换。 PV 操作是低级通信方式,高级通信方式是指以较高的效率传输大量数据的通信方式。高级通信方法主要有以下三类:
- 共享存储。在通信的进程之间存在一块可直接访问的共享空间,通过对这片共享空间进行 写 / 读 操作实现进程之间的信息交换。在对共享空间进行 写 / 读 操作时,需要使用同步互斥工具(如 P 操作、 V 操作),对共享空间进行 写 / 读 控制。共享存储又分为两种:低级方式的共享是基于数据结构的共享。高级方式的共享则是基于存储区的共享。操作系统只负责位进程通信提供可共享使用的存储空间和同步互斥工具,而数据交换则由用户自己安排 写 / 读 指令完成。
- 消息传递。在消息传递系统中,进程间的数据交换是以格式化的消息为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。 (1)直接通信方式。发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接受进程从消息缓冲队列中取得消息。 (2)间接通信方式。发送进程把消息发送到某个中间实体,接受进程从中间实体取得消息。这种中间实体一般称为信箱,这种通信方式又称信箱通信方式。
- 管道通信。管道通信是消息传递的一种特殊方式。所谓“管道”,是指用连接一个读进程和一个写进程以实现它们之间的通信的一个共享文件,又名 pipe 文件。向管道(共享文件)提供输入的发送进程(即写进程),以字符流形式将大量的数据送入(写)管道;而接收管道输出的接收进程(即读进程)则从管道中接收(读)数据。为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方的存在。管道是一个固定大小的缓冲区。 从管道读数据是一次性操作,数据一旦被读取,它就从管道中被抛弃,释放空间以便写更多的数据。 管道只能采用半双工通信,即某一时刻只能单向传输。要实现父子进程双方互动通信,需定义两个管道
线程是进程中的一个实体,是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属一个进程的其它线程共享进程所拥有的全部资源。
一个线程可以创建和撤销另一个线程,同一进程的多个线程之间可以并发执行。由于线程之间的相互制约,致使线程在运行中呈现出间断性。
线程也有就绪、阻塞和运行三种基本状态。
若线程的切换发生在同一个进程内部,则只需要很少的时空开销。
线程与进程的比较:
- 调度。在传统的操作系统中,拥有资源和独立调度的基本单位都是进程。在引入线程的操作系统中,线程是独立调度的基本单位,进程是拥有资源的基本单位。在同一进程中,线程的切换不会引起进程切换。在不同进程中进行线程切换会引起进程切换。
- 拥有资源。进程是拥有资源的基本单位,而进程不拥有系统资源,但线程可以访问其隶属进程的系统资源。
- 并发性。进程之间可以并发执行,多个线程之间也可以并发执行,提高了系统的吞吐量。
- 系统开销。创建或撤销进程时操作系统所付出的开销远大于创建或撤销线程时的开销。进线程切换同样。
- 地址空间和其他资源(如打开的文件)。进程间的地址空间之间相互独立,同一进程的各线程间共享进程的资源,某进程内的线程对于其他进程不可见。
- 通信方面。进程间通信(IPC)需要进程同步和互斥手段的辅助,以保证数据的一致性,而线程间可以直接 读 / 写 进程数据段(如全局变量)来进行通信。
线程的实现可以分为两类:用户级线程和内核级线程。内核级线程又称为内核支持的线程。
在用户级线程中,有关线程管理的所有工作都应有用户程序完成,内核意识不到线程的存在。应用程序可以通过使用线程库设计成多线程程序。通常,应用程序从单线程开始,在该线程中开始运行,在其运行的任何时刻,可以通过调用线程库中的派生例程创建一个在相同进程中运行的新线程。
在内核级线程中,线程管理的所有工作由内核完成,应用程序没有进行线程管理的代码,只有一个到内核级线程的编程接口。内核为进程及其内部的每个线程维护上下文信息,调度也在内核基于线程架构的基础上完成。
C 语言编写的程序在使用内存时一般分为三个段,它们一般是正文段(即代码和赋值数据段)、数据堆段和数据栈段。二进制代码和常量存放在正文段,动态分配的存储区在数据堆段,临时使用的变量在数据栈段。
系统进程所请求的一次 I/O 操作完成后,将使进程状态从阻塞态变为就绪态。
同一个系统的进程(或线程)可以由系统调用的方法被不同的进程(或线程)多次使用。
处理机调度
处理机调度时多道操作系统的基础,是操作系统设计的核心问题。
一个作业从提交开始直到完成,往往要经历以下三级调度:
- 高级调度。又称作业调度,其主要任务是按一定的原则从外存上处于后备状态的作业中挑选一个(或多个)。给它(们)分配内存、输入/输出设备等必要的资源,并建立相应的进程,以使它(们)获得竞争处理机的权利。简言之,作业调度就是内存与辅存之间的调度。对于每个作业只调入一次、调出一次。
- 中级调度。又称内存调度又称内存调度,其作用是提高内存利用率和系统吞吐量·。为此,应将那些暂时不能运行的进程调至外存等待,把此时的进程状态称为挂起态。当它们已具备运行条件且内存又稍有空闲时,由中级调度来决定把外存那些已具备运行条件的就绪进程再重新调入内存,并修改其状态为就绪态,挂在就绪队列上等待。
- 低级调度。又称进程调度,其主要任务是按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它。进程调度是操作系统中最基本的一种调度,再一般的操作系统中都必须配置进程调度,进程调度的频率和高,一般几十毫秒一次。
作业调度为进程活动做准备,进程调度使进程正常活动起来,内存调度将暂时不能运行的进程挂起,内存调度处于作业调度和进程调度之间。
作业调度次数少,内存调度次数略多,进程调度频率最高。
进程调度是最基本的,不可或缺。
现代操作系统中,不能进行进程的调度与切换的情况由以下几种:
- 在处理机中断的过程中。
- 进程在操作系统内核程序临界区中。
- 其他需要完全屏蔽中断的原子操作过程中。
周转时间 = 作业完成时间 - 作业提交时间(就是到达时间)
平均周转时间 = (作业1的周转时间 + ... + 作业n的周转时间)/ n
带权周转时间 = (作业周转时间 / 作业实际运行时间)>= 1
平均带权周转时间 = (作业1的带权周转时间 + ... + 作业n的带权周转时间)/ n
典型的调度算法:
- 先来先服务(FCFS)调度算法。既可用于作业调度,又可用于进程调度。FCFS 属于不可剥夺算法。对长作业比较有利,但对短作业不利(相对于 SJF 和高响应比);有利于 CPU 繁忙型作业(需要大量的 CPU 时间进行计算,而很少请求 I/O 操作),而不利于 I/O 繁忙型作业(CPU 处理时,需频繁地请求 I/O 操作)。
- 短作业优先(SJF)调度算法。短进程优先(SPF)调度算法。作业/进程调度。对长作业不利,有可能导致长作业饥饿现象。
- 优先级调度算法。作业/进程调度。 根据新的更高级进程能否抢占正在执行的进程,可将该调度分为如下两种:(1)非剥夺式优先级调度算法。当一个进程正在处理机上运行时,即使由某个更为重要或紧迫的进程进入就绪队列,仍然让正在运行的进程继续运行,直到由于其自身原因而主动让出处理机时(任务完成或等待事件),才把处理机分配给更重要或紧迫的进程。(2)剥夺式优先级调度算法。当一个进程正在处理机上运行时,若有某个更为重要或紧迫的进程进入就绪队列,则立即暂停正在运行的进程,将处理机分配给更重要或紧迫的进程。 根据进程创建后其优先级是否可以改变,可以将进程分为以下两种:(1)静态优先级。优先级是在创建进程时确定的,且在进程的整个运行期间保持不变。(2)动态优先级。在进程运行过程中,根据进程情况动态调整优先级。
- 高响应比优先调度算法。在每次进行作业调度时,先计算后备作业队列中每个作业的相应比,从中选出相应比最高的作业投入运行。 相应比 RP = (等待时间 + 要求服务时间)/ 要求服务时间。克服了饥饿状态,兼顾了长作业。
- 时间片轮转调度算法。主要适用于分时系统。时间片的大小对系统性能的影响很大,若时间片足够大,以至于所有进程都能在一个时间片内执行完毕,则时间片轮转调度算法就退化为先来先服务调度算法。若时间片很小,则处理机将在进程间过于频繁地切换,使处理机开销增大,而真正用于运行用户进程的时间 将减少。因此,时间片的大小应选择适当。
- 多级反馈队列调度算法(融合了前几种算法的优点)。(1)设置多个就绪队列,并将各个 队列赋予不同的优先级,第1队列的优先级最高,第2级队列次之,其余队列 的优先级逐次降低。(2)赋予各个队列中进程的执行时间片的大小各不相同。在优先级高的队列中,每个进程的运行时间片越小。(3)一个新进程进入内存后,首先将它放入第一级队列的末尾,按 FCFS 原则排队等待调度。当轮到该进程执行时,如它能在该时间片内完成,便可准备撤离系统;若它在一个时间片结束时尚未完成,调度程序便将该进程转入第2级队列的末尾,再同样按 FCFS 原则等待调度执行…… (4)当且仅当第1级队列为空时,调度程序才调度第2级队列中的进程运行;仅当第1~( i-1 )级队列均为空时,才会调度第i级队列中的进程运行。若处理机正在执行第i级队列中的某进程,这时又有新进程进入优先级更高的队列【第1~( i-1 )中的任何一个队列】,则此时新进程将抢占正在运行进程的处理机,即由调度程序把正在运行的进程放回第i级队列的末尾(优先级不变),把处理机分配给新到的更高优先级的进程。
时间片调度算法是绝对可抢占的。分时系统的时间片固定,因此用户数越多,响应时间越长。
中断向量本身是用于存放中断服务例行程序的入口地址,因此中断向量地址就应是该入口地址的地址。
进程同步
将一次仅允许一个进程使用的资源称为临界资源(互斥、共享)。对临界资源的访问,必须互斥进行,在每个进程中,访问临界资源的那段代码称为临界区。
同步亦称直接制约关系,是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而等待、传递信息所产生的制约关系。进程间的直接制约关系源于它们之间的相互合作。
互斥也称间接制约关系。当一个进程进入临界区使用临界资源时,另一个进程必须等待,当占用临界资源的进程退出临界区后,另一进程在允许去访问此临界资源。
为禁止两个进程同时进入临界区,同步机制应遵循以下准则:
- 空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。
- 忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待。
- 有限等待。对请求访问的进程,应保证能在有限时间内进入临界区。
- 让权等待。当进程不能进入临界区时,应立即释放处理器,防止进程忙等待。
系统中的各种硬件资源和软件资源,均可用数据结构抽象地描述其资源 特性,即用少量信息和对资源所执行的操作来表示该资源,而忽略它们的内部结构和实现细节。管程是由一组数据及定义在这组数据之上的对这组数据的操作而组成的软件模块,这组操作能初始化并改变管程中的数据和同步进程。
管程的组成:
- 局部于管程共享的数据结构说明。
- 对该数据结构进行操作的一组过程。
- 对局部于管程的共享数据设置初始值的语句。
管程的基本特性:
- 局部于管程的数据只能被局部于管程内的过程所访问。
- 一个进程只有通过调用管程内的进程才能进入管程访问共享数据。
- 每次仅允许一个进程在管程内执行某个内部过程。
在操作系统中,P、V 操作时一种低级进程通信原语。
可以被多个进程在任意时刻共享的代码必须是不允许任何修改的代码(可重入代码)。若代码可被多个进程在任意时刻共享,则要求任一个进程在调用此段代码时都以同样的方式运行;而且进程在运行过程中被中断后再继续执行,其执行结果不受影响。这必然要求代码不能被任何进程修改,否则无法满足共享的要求。
管程的 signal 操作与信号量机制中的 V 操作不同,信号量机制中的 V 操作一定会改变信号量的值 S = S + 1 。而管程中的 signal 操作是针对某个条件变量的,若不存在因该条件而阻塞的进程,则 signal 不会产生任何影响。
硬件方法(关中断、硬件指令方法)实现进程同步时不能实现让权等待。
死锁
所谓死锁,是指多个进程因竞争资源而造成的一种僵局(互相等待),若无外力作用,这些进程都将无法向前推进。
死锁产生的原因:
- 系统资源的竞争。
- 进程推进顺序非法。
- 死锁产生的必要条件。产生死锁必须同时满足以下4个,只要其中任意一个条件不成立,死锁就不会发生。(1)互斥:进程要求对所分配的资源进行排他性控制,即在一段时间内某资源仅为一个进程所占有。此时若有其他进程请求该资源,则请求进程只能等待。 (2)不剥夺条件:进程所获得的资源在未使用完之前,不能被其他进程强行夺走,即只能由获得资源的进程自己来释放(只能是主动释放)。 (3)请求并保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源已被其他进程所占有,此时请求进程被阻塞,但对自己已获得的资源保持不放。 (4)循环等待条件:存在一种进程资源的循环等待链,链中每个进程已获得的资源同时被链中下一个进程所请求。
死锁的处理策略:
- 死锁预防:设置某些限制条件,破坏产生死锁的4各必要条件中的一个或几个,以防止发生死锁。
- 避免死锁:在资源的动态分配过程中,用某种方法防止系统进入不安全状态,从而避免死锁。
- 死锁的检测及解除:无须采取任何限制性措施,允许进程在运行过程中发生死锁,通过系统的检测机构及时地检测出死锁的发生,然后采取某种措施解除死锁。
资源分配策略 | 各种可能模式 | 主要优点 | 主要缺点 | |
死锁预防 | 保守,宁可资源闲置 | 一次请求所有资源(破坏请求并保持条件),资源剥夺(破坏不剥夺条件),资源按序分配(破坏循环等待条件) | 适用于突发式处理的进程,不必进行剥夺 | 效率低,进程初始化时间延长;剥夺次数过多;不便灵活申请资源 |
死锁避免 | 是“预防”和“检测”的折中(在运行时判断是否可能死锁) | 寻找可能的安全允许顺序(银行家算法) | 不必进行剥夺 | 必须知道将来的资源需求;进程不能长时间阻塞 |
死锁检测 | 宽松,只要允许就分配资源 | 定期检查死锁是否已经发生(资源分配图,死锁定理,死锁解除(资源剥夺法,撤销进程法,进程回退法)) | 不延长进程初始化时间,允许对死锁进行现场处理 | 通过剥夺解除死锁,造成损失 |
以上内容均来自王道书籍及相关课程等