进程
程序:是静态的,就是个存放在磁盘里的可执行文件,就是一系列的指令集合 进程:是动态的,是程序的一次执行过程。同一个程序多次执行会对应多个进程
进程和程序的区别
- 程序是永存的,进程是暂时的,是程序的一次执行过程,有创建有撤销
- 程序是静态的,进程是动态的
- 进程具有并发性,而程序没有
- 进程是竞争计算机资源的基本单位,程序不是
- 进程和程序不是一一对应的:一个程序可对应多个进程,即多个进程可执行同一程序,而一个进程又可以执行一个或几个程序
进程的组成
PCB
当进程被创建时,操作系统会为该进程分配一个唯一的、不重复的PID,相当于进程的身份证号 操作系统要记录PID、进程所属用户ID(UID),还要记录给进程分配了哪些资源(如分配了多少内存、正在使用哪些I/O设备、正在使用哪些文件),还要记录进程的运行情况(如:CPU使用时间、磁盘使用情况、网络流量使用情况等) 这些信息都被保存在一个数据结构PCB,即进程控制块中
程序段、数据段
PCB是给操作系统用的,是进程存在的唯一标志 程序段、数据段是给进程自己用的,与进程自身的运行逻辑有关
程序是如何运行的
进程的特征
进程的状态
创建态:进程正在被创建时,它的状态是创建态,在这个阶段操作系统会为进程分配资源、初始化PCB
就绪态:当进程创建完成后,便进入就绪态,处于就绪态的进程已经具备运行条件,但由于没有空闲CPU,就暂时不能运行
运行态:如果一个进程此时在CPU上运行,那么这个进程处于运行态,CPU会执行该进程对应的程序(执行指令序列)
阻塞态:在进程运行的过程中,可能会请求等待某个事件的发生(如等待某种资源的分配,或者等待其他进程的响应)。在这个事件发生之前,进程无法继续往下执行,此时操作系统会让这个进程下CPU,并让它进入阻塞态,当CPU空闲时,又会选择另一个就绪态进程上CPU运行
终止态:一个进程可以执行exit系统调用,请求操作系统终止该进程。此时该进程会进入终止态,操作系统会让该进程下CPU,并回收内存空间等资源,最后还要回收该进程的PCB。
进程的组织
链接方式 索引方式
进程控制
进程控制的主要功能是对系统中的所有进程实施有效的管理,它具有创建新进程、撤销已有进程、实现进程状态转换等功能。
通常使用原语实现进程控制 原语是一种特殊的程序,它的执行具有原子性,也就是说这段程序的运行必须一气呵成,不可中断
如何实现原语的原子性 主要是利用关中断指令和开中断指令这两个特权指令实现原子性 CPU执行了关中断指令之后,就不再检查中断信号,直到执行开中断指令才会恢复检查。这样,关中断、开中断之间的这些指令序列就是不可被中断的,实现了原子性
进程控制相关的原语
进程间通信(重点)
进程间通信是指两个进程之间产生数据交互
共享存储
基于存储区的共享:操作系统在内存中划出一块共享存储区,数据的形式、存放位置都由通信进程控制,而不是操作系统。这种共享方式速度很快,是一种高级通信方式 基于数据结构的共享:比如共享空间里只能放一个长度为10的数组。这种共享方式速度慢、限制多,是一种低级通信方式
消息传递
进程间的数据交换以格式化的消息为单位。进程通过操作系统提供的 “发送消息/接收消息"两个原语进行数据交换
格式化的消息: 传递方式 直接通信方式 间接通信方式
管道通信(遵循先进先出原则)
小结
线程
可以把线程理解为轻量级进程。首先,线程是一个基本的CPU执行单位,也是程序执行流的最小单位。其次,引入线程之后,不仅是进程之间可以并发,进程内的各线程之间也可以并发,从而进一步提升了系统的并发度,使得一个进程内也可以并发处理各种任务。另外,引入线程后,进程只作为除CPU之外的系统资源的分配单元(如打印机、内存地址空间等都是分配给进程的)
引入线程机制后带来的变化
线程的属性
线程的实现方式
用户级线程 内核级线程
多线程模型
一对一模型 多对一模型 多对多模型
小结
调度(重点)
高级调度(作业调度)
作业:一个具体的任务 解决的问题:好几个程序需要启动,到底先启动哪个
按一定的原则从外存的作业后备队列中挑选一个作业调入内存,并创建进程,每个作业只调入一次,调出一次。作业调入时会建立PCB,调出时才撤销PCB
低级调度(进程调度/处理机调度)
按照某种策略从就绪队列中选取一个进程,将处理机分配给它。 进程调度是操作系统中最基本的一种调度,在一般的操作系统中都必须配置进程调度。进程调度的频率很高,一般几十毫秒一次
中级调度(内存调度)
内存不够时,可将某些进程的数据调出外存。等内存空闲或者进程需要运行时再重新调入内存。暂时调到外村等待的进程状态为挂起状态。被挂起的进程的进程PCB会被组织成挂起队列。而中级调度就是按照某种策略决定将哪个处于挂起状态的进程重新调入内存。一个进程可能会被多次调出、调入内存,因此中级调度发生的频率要比高级调度更高。
引入进程七状态模型
三层调度的联系、对比
进程调度的时机、切换与过程、方式
进程调度的方式
进程的切换与过程
调度算法的评价指标
CPU利用率:指CPU“忙碌”的时间占总时间的比例
系统吞吐量:单位时间内完成作业的数量
周转时间:指从作业提交给系统开始,到作业完成为止的这段时间间隔 等待时间:指进程/作业处于等待处理机状态时间之和,等待时间越长,用户满意度越低
响应时间:指从用户提交请求到首次产生响应所用的时间
进程调度算法
先来先服务(FCFS)
按照请求的顺序进行调度。非抢占式,开销小,无饥饿问题,响应时间不确定,可能很慢,对短进程不利,对IO密集型进程不利。
短作业优先(SJF)
按估计运行时间最短的顺序进行调度。非抢占式,吞吐量高,开销可能较大,可能导致饥饿问题,对短进程提供好的响应时间,对长进程不利
高响应比优先(HRRN)
同时考虑了等待时间的长短和要求服务的时间长短,很好地平衡了长短进程,非抢占式,吞吐量高,开销可能较大,提供好的响应时间,无饥饿问题。
时间片轮转(RR)
将所有就绪进程按先到先服务的原则排成一个队列,用完时间片的进程排到队列最后。抢占式(时间片用完时),开销小,无饥饿问题,为短进程提供好的响应时间,若时间片小,进程切换频繁,吞吐量低;若时间片太长,实时性得不到保证。
优先级调度
为每一个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级
多级反馈队列调度算法
设置多个就绪队列,优先级递减,时间片递增。只有等到优先级更高的队列为空时才会调度当前队列中的进程。如果进程用完了当前队列的时间片还未执行完,则会被移到下一队列。它是抢占式的,开销可能较大,对IO型进程有利,可能会出现饥饿问题
同步与互斥(重点)
同步是一种直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调它们的工作次序而产生的制约关系。进程间的直接制约关系源于它们之间的相互合作
互斥是一种间接制约关系,进程互斥指当一个进程访问某临界资源时,另一个想要访问该临界资源的进程必须等待。当访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问临界资源。
进程互斥的四个代码逻辑部分
1、进入区:检查是否可以进入临界区,若可进入,需要“上锁” 2、临界区:访问临界资源的那段代码 3、退出区:负责“解锁” 4、剩余区:其余带啊吗部分
进程互斥需要遵循的四个原则
1、空闲让进。临界区空闲时,可以允许一个请求进入临界区的进程立即进入临界区。 2、忙则等待。当已有进程进入临界区时,其他试图进入临界区的进程必须等待。 3、有限等待。对请求访问的进程,应保证能在有限时间内进入临界区(保证不会饥饿)。 4、让权等待。当进程不能进入临界区时,应立即释放处理机,防止进程忙等待。
进程互斥的软件实现方法
单标志法
算法思想:两个进程在访问完临界区后会把使用临界区的权限转交给另一个进程。也就是说每个进程进入临界区的权限只能被另一个进程赋予
存在的问题:这是一种轮流访问的算法,如果某个时刻允许A访问,但A一直不访问,此时B也不能访问,就违背了空闲让进原则
双标志先检查法
算法思想:设置一个布尔数组flag[],数组中各个元素用来标记各进程想进入临界区的意愿,比如"flag[0]=true"意味着0号进程现在想进入临界区,另一个进程 想访问时,把对应的flag[i]设为true
存在的问题:进程并发执行时,会出现同时访问临界区,违反了忙则等待的原则
双标志后检查法
算法思想:双标志先检查法是先检查另一个进程要不要访问临界区后把flag[i]设为true上锁,后检查法则先把flag[i]设为true上锁,后检查另一个进程要不要访问临界区
存在的问题:虽然解决了忙则等待的问题,但如果两个进程并发,同时想要访问临界区,就会导致两个都不能访问临界区,违背了空闲让进和有限等待原则
Peterson算法
算法思想:结合双标志法、单标志法的思想。如果双方都想进入临界区,那可以让进程尝试谦让。比如有A和B,A表示想访问的意愿,然后主动谦让给B先使用,如果B想使用并且自己也谦让了,A就等待,B使用;如果B也谦让,则A就可以使用
存在的问题:遵循了空闲让进、忙则等待、有限等待三个原则,但是如果进程一直不访问临界区,就会不断地循环检查,违背了让权等待地原则
进程互斥的硬件实现方法
中断屏蔽法
利用开/关中断指令实现,即在某个进程开始访问临界区到结束访问为止都不允许被中断,就不会发生进程切换,不会有两个进程同时访问临界区的情况
优点是简单高效 缺点是不适用多处理机,只适用于操作系统内核进程,不适用于用户进程,因为开/关中断指令只能运行在内核态
TestAndSet指令
用硬件实现,执行的过程不允许被中断,只能一气呵成
用一个lock变量表示当时临界区是否被加锁,再用一个old变量来存放lock原来的值,然后把lock设为true,返回old,用TS指令检查是否上锁,如果是true就等待,如果是false就访问临界区
优点是实现简单,无需像软件实现方法那样严格检查是否会有逻辑漏洞;适用于多处理机环境 缺点是不满足让权等待原则,暂时无法进入临界区的进程会占用CPU并循环执行TS指令,从而导致忙等
Swap指令
用硬件实现,执行的过程不允许被中断,只能一气呵成,逻辑上和TS指令无太大区别
信号量机制(重点)
P、V操作
P、V操作对应wait(S)、signal(S)原语,用于实现系统资源的申请和释放
S.value的初值表示系统中某种资源的数目
对信号量S的一次P操作意味着进程请求一个单位的该类资源,因此需要执行S.value–,表示资源数减1,当S.value<0时表示该类资源已分配完毕,因此进程应调用block原语进行自我阻塞,主动放弃处理机,并插入该类资源的等待队列中
对信号量S的一次V操作意味着进程释放一个单位的该类资源,因此需要执行S.value++,表示资源数加1,若加1后仍是S.value<=0,表示依然有进程在等待该类资源,因此应调用wakeup原语唤醒等待队列中的第一个进程
信号量机制实现进程互斥
设置互斥信号量mutex,表示进入临界区的名额,初值为1,整个过程就是申请资源,进入临界区,释放资源
信号量机制实现进程同步
假设有两句代码需要一前一后执行,先设置同步信号量S,初值为0,在前执行的代码之后执行V操作,在后执行的代码之前执行P操作,因为刚开始没有这种资源,必须由前执行的代码执行完之后产生这种资源,才能执行P操作,进而执行后执行的代码,这样就实现了两句代码一前一后执行
信号量机制实现前驱关系
画出前驱图,把每一对前驱关系都看成一个同步问题 为每一对前驱关系设置同步信号量,初值为0 在每个前操作之后执行V操作,在每个后操作之前执行P操作
生产者-消费者问题(重点)
问题分析
系统中有一组生产者进程和一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区取一个产品并使用,这里的产品理解某种数据
生产者、消费者共享一个初始为空、大小为n的缓冲区,这里的缓冲区是临界资源,各进程必须互斥地访问
只有缓冲区没满时,生产者才能把产品放入缓冲区,否则必须等待 只有缓冲区不空时,消费者才能从中取出产品,否则必须等待
注意: 实现互斥的P操作一定要在实现同步的P操作之后,否则会发生死锁问题
V操作不会导致进程阻塞,因此两个V操作顺序可以交换
生产者生产一个产品和消费者消费一个产品逻辑上可以放到P、V操作之间,但这样会导致访问临界资源时间过长,影响性能
多生产者-多消费者问题(重点,多指多类别)
问题分析
桌子上有一只盘子,每次只能向其中放入一个水果。父亲专向盘子中放苹果,母亲专向盘子中放橘子,儿子专等着吃盘子中的橘子,女儿专等着吃盘子中的苹果。只有盘子空时,父亲或母亲才可向盘子中放一个水果。仅当盘子中有自己需要的水果时,儿子或女儿可以从盘子中取出水果
设置互斥信号量的实现 不设置互斥信号量的实现 注意:只有缓冲区大小为1时,才能不设置互斥信号量;如果缓冲区大小大于1,必须设置互斥信号量来访问临界区,否则会出现两个进程写入缓冲区的数据相互覆盖的情况
吸烟者问题
问题分析
假设一个系统有三个抽烟者进程和一个供应者进程。每个抽烟者不停地卷烟并抽掉它,但是要卷起并抽掉一支烟,抽烟者需要有三种材料:烟草、纸和胶水。三个抽烟者中,第一个拥有烟草、第二个拥有纸、第三个拥有胶水。供应者进程无限地提供三种组合材料,供应者每次将两种材料放桌子上,拥有剩下那种材料的抽烟者卷一根烟并抽掉它,并给供应者进程一个信号告诉完成了,供应者就会放另外两种材料再桌上,这个过程一直重复(让三个抽烟者轮流地抽烟)
读者写者问题(重点)
有读者和写者两组并发进程,共享一个文件,当两个或两个以上的读进程同时访问共享数据时不会产生副作用,但若某个写进程和其他进程(读进程和写进程)同时访问共享数据时则可能导致数据不一致的错误。因此要求:允许多个读者可以同时对文件执行读操作;只允许一个写者往文件中写信息;任一写者在完成写操作之前不允许其他读者或写者工作;写者执行写操作前,应让已有的读者和写者全部退出。
读写公平算法
哲学家进餐问题(重点)
问题分析
一个圆桌上坐着5名哲学家,每两个哲学家之间的桌上摆一根筷子,桌子的中间是一碗米饭。哲学家们倾注毕生的精力用于思考和进餐,哲学家在思考时,并不影响他人。只有当哲学家饥饿时,才试图拿起左、右两根筷子(一根一根地拿起)。如果筷子已在他人手上,则需等待。饥饿的哲学家只有同时拿起两根筷子才开始进餐,当进餐完毕后,放下筷子继续思考
系统有5个哲学家进程,5位哲学家与左右邻居对其中间筷子的访问是互斥关系。每个哲学家需要同时持有两个临界资源才能开始吃饭 先定义互斥信号数组,用于实现对5个筷子的互斥访问。并对哲学家按0-4编号,哲学家i左边的筷子编号为i,右边的筷子编号为(i+1)%5,具体代码实现如下
管程
定义和基本特征
死锁(重点)
概念
在并发环境下,各进程因竞争资源而造成的一种互相等待对方手里的资源,导致各进程都阻塞,都无法向前推进的现象,就是死锁,发生死锁后如果无外力干涉,这些进程都将无法向前推进。
死锁、饥饿、死循环的区别
死锁产生的必要条件
互斥条件:只有对必须互斥使用的资源的争抢才会导致死锁(如哲学家的筷子、打印机设备)。像内存、扬声器这样可以同时让多个进程使用的资源是不会导致死锁的,因为进程不用阻塞等待这种资源。
不剥夺条件:进程所获得的资源在未使用完之前,不能由其他进程强行夺走,只能主动释放。
请求和保持条件:进程已经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程占有,此时请求进程被阻塞,但又对自己已有的资源保持不放。
循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进程所请求。
什么时候会发生死锁
1、对系统资源的竞争。各进程对不可剥夺的资源(如打印机)的竞争可能引起死锁,对可剥夺的资源,例如CPU的竞争是不会引起死锁的。 2、进程推进顺序非法。请求和释放资源的顺序不当,也同样会导致死锁。例如,并发执行的进程P1、P2分别申请的资源R2,而进程R2又申请资源R1.两者会因为申请的资源被对方占有而阻塞,从而发生死锁 3、信号量的使用不当也会造成死锁。如生产者-消费者问题中,如果实现互斥的P操作在实现同步的P操作之前,就有可能导致死锁
死锁的处理策略
预防死锁(破坏四个必要条件中的任一个)
破坏互斥条件 把只能互斥使用的资源改造成允许共享使用,比如SPOOLing技术,把独占设备在逻辑上改造成共享设备
缺点:并不是所有的资源都可以改造成可共享使用的资源,很多时候无法破坏互斥条件
破坏不可剥夺条件 方案一:当某个进程请求新的资源得不满足时,它必须立即释放保持的所有资源,待以后需要时再重新申请。也就是说,即使某些资源尚未使用完,也需要主动释放,从而破坏了不可剥夺条件 方案二:当某个进程需要的资源被其他进程所占有的时候,可以由操作系统协助,将想要的资源强行剥夺。这种方式一般需要考虑各进程的优先级(比如:剥夺调度方式,就是将处理机资源强行剥夺给优先级更高的进程使用)
缺点: 1、实现起来比较复杂 2、释放已获得的资源可能导致前一阶段的工作失效。因此这种方法一般只适用于易保存和恢复状态的资源,如CPU 3、反复地申请和释放资源会增加系统开销,降低系统吞吐量 4、若采用方案一,意味着只要暂时得不到某个资源,之前获得地那些资源就都需要放弃,以后再重新申请。如果一直发生这样的情况,就会导致进程饥饿
破坏请求和保持条件 可以采用静态分配方法,即进程在运行前一次申请完它所需要的全部资源,在它的资源未满足前,不让它投入运行。一旦投入运行后,这些资源就一直归它所有,该进程就不会再请求别的任何资源了
缺点: 有些资源可能只需要用很短的时间,因此如果进程的整个运行期间都一直保持着所有资源,就会造成严重的浪费,资源利用率极低。另外,该策略也有可能导致某些进程饥饿。
破坏循环等待条件 可采用顺序资源分配法。首先给系统中的资源编号,规定每个进程必须按编号递增的顺序请求资源,同类资源(即编号相同的资源)一次性申请完。 原理分析:一个进程只有已占有小编号的资源时,才有资格申请更大编号的资源。按此规则,已持有大编号资源的进程不可能逆向地来回申请小编号的资源,从而就不会产生循环等待的现象
缺点: 1、不方便增加新的设备,因为可能需要重新分配所有的编号 2、进程实际使用资源的顺序可能和编号递增顺序不一致,会导致资源浪费 3、必须按规定次序申请资源,用户编程麻烦
避免死锁
安全序列
如果系统按照这种序列分配资源,则每个进程都能顺利完成。只要能找出一个安全序列,系统就是安全状态。 如果分配了资源之后,系统中找不出任何一个安全序列,系统就进入了不安全状态。这就意味着之后可能所有进程都无法顺利的执行下去。如果有进程提前归还了一些资源,那系统也有可能重新回到安全状态,不过我们在分配资源之前总是要考虑到最坏的情况。 如果系统处于安全状态,就一定不会发生死锁。如果系统进入不安全状态,就可能发生死锁(处于不安全状态未必就是发生了死锁,但发生死锁时一定会是在不安全状态) 因此可以在资源分配之前预先判断这次分配是否会导致系统进入不安全状态,以此决定是否答应资源分配请求,这就是银行家算法的核心思想
银行家算法
主要思想是避免系统进入不安全状态,在每次进行资源分配时,它首先检查系统是否有足够的资源满足要求,如果有,则先试着分配,并对分配后的新状态进行安全性检查。如果新状态安全,则正式分配上述资源,否则拒绝分配上述资源。这样就保证系统始终处于安全状态,从而避免死锁现象的发生。
死锁的检测和解除
检测死锁的算法
(1)在资源分配图中,找出既不阻塞又不是孤立的进程Pi,即找出一条有向边与它相连,且该有向边对应资源的申请数量小于等于该系统中已有空闲资源数量。如下图中,R1没有空闲资源,R2有一个空闲资源。若所有的连接该进程的边均满足上述条件,则这个进程能继续运行直至完成,然后释放它所占有的资源。消去它所有的请求边和分配边,使之成为孤立的节点。在下图中,P1是满足这一条件的进程节点,于是可以将P1的所有边消去。 (2)进程Pi所释放的资源,可以唤醒某些因等待这些资源而阻塞的进程,原来的阻塞进程可能变为非阻塞进程。在下图中,P2就满足这样的条件。根据(1)中的方法进行一系列简化后,若能消去途中所有的边,则称该图是可完全简化的。
死锁定理
如果资源分配图是可以完全简化的,即能消去所有的边,则没有死锁
死锁的解除
1、资源剥夺法。 挂起(暂时放到外存上)某些死锁进程,并抢占它的资源,将这些资源分配给其他的死锁进程。但是应防止被挂起的进程长时间得不到资源而饥饿
2、撤销进程法(终止进程法) 强制撤销部分、甚至全部死锁进程,并剥夺这些进程的资源。这种方式的优点是实现简单,但付出的代价可能会很大。因为有些进程可能已经运行了很长时间,已经接近结束了,一旦被终止可能功亏一篑,还得重头再来
3、进程回退法 让一个或多个死锁进程回退到足以避免死锁的地步。这就要求系统要记录进程的历史信息,设置还原点。
可以从进程优先级、已执行多长时间、还要多久能完成、进程已经使用了多少资源、进程是交互式的还是批处理式的等方面来决定处理哪部分进程
|