一、进程的定义、组成、组织方式、特征
1.1 进程的定义
-
程序:一个指令序列,即指令(或语句)的集合。
-
程序的顺序执行:指令之间是顺序关系,是一个静态的概念,仅当前一操作(程序段)执行完后,才能执行后继操作。
-
顺序执行的特点:
- 顺序性;处理机的操作严格按照程序所规定的顺序执行
- 封闭性;程序独占全机资源,程序执行结果不受外界因素的影响
- 可再现性;只要输入的初始条件相同,则无论何时重复执行该程序都会得到相同的结果。
-
程序的并发执行的特征:
- 间断性:任意程序不可能一直占有CPU
- 失去封闭性:多个程序共享系统中的各种资源,因而这些资源的状态将由多个程序来改变,致使程序的运行失去了封闭性。
- 不可再现性:程序在并发执行时,由于失去了封闭性,也导致失去了可再现性。
-
进程与程序的关系:前者是一种动态概念,而后者是一种静态概念,进程是程序的一次执行
-
PCB:进程控制块,用来描述进程的各种信息。因为PCB经常被系统访问,所以应常驻内存。
-
进程实体:由PCB、程序段、数据段三部分组成,又称为进程
-
进程定义:进程是进程实体的运行过程,是系统进行资源分配和调度的一个独立单位
-
PCB与进程的关系:PCB是进程存在的唯一标志。所谓创建进程,即创建进程实体中的PCB;撤销进程即撤销进程实体中的PCB
-
进程的基本属性:进程是一个可拥有资源的独立单位;进程又是一个可独立调度的基本单位。
1.2 进程(实体)的组成
- PCB:包括进程描述信息、进程控制和管理信息、资源分配清单、处理机相关信息
- 程序段:存放要执行的代码
- 数据段:存放程序运行过程中处理的各种数据
1.3 进程的组织方式
- 线性方式:按照进程状态将PCB分为多个队列,如就绪队列、阻塞队列等,操作系统持有指向各个队列的指针
- 索引方式:根据进程状态的不同建立几张索引表,如就绪索引表、阻塞索引表等,操作系统持有指向各个索引表的指针
1.4 进程的特征
- 动态性:进程是进程实体的一次执行过程(动态性),有一定的生命周期(由创建而产生,由调度而执行,由撤消而消亡)。
- 并发性:多个进程同存于内存中,且能在一段时间内同时运行。
- 独立性: 进程是一个能独立运行,独立分配资源和独立接受调度的基本单位。
- 异步性:进程按各自独立的、不可预知的速度向前推进。
- 结构性:从结构上看,进程实体至少包括: 程序、数据和进程控制块(PCB) 。
二、进程的状态与转换
2.1 进程的五种状态
- 运行态:占有CPU、并在CPU上运行
- 就绪态:已具备运行条件(已分配到除CPU以外的所有必要资源),但由于没有空闲CPU而暂时不能运行
- 阻塞态:因等待某一事件而暂时不能运行,放弃CPU
- *创建态:进程正在被创建,操作系统为进程分配资源,初始化PCB
- *终止态:进程正在从系统中撤销,操作系统会回收进程拥有的资源,撤销PCB
- 补充:挂起态:挂起进程即把进程放在外存中
- 引入挂起状态的原因
- (1) 终端用户的请求。暂停进程的执行,修改程序。
- (2) 父进程请求。 挂起自己的子进程。
- (3) 负荷调节的需要。把不重要的进程挂起,避免系统负荷较重。
- (4) 操作系统的需要。 检查资源使用情况等。
2.2 进程状态的转换
加入挂起态以后
三、进程控制
3.1 操作系统内核
- 内核定义:计算机上配置的底层软件,是操作系统最基本、最核心的部分。
- 操作系统分为非内核部分和内核部分
- 内核目的:便于对软件进行保护,防止遭到其他应用程序的破坏;提高OS的运行效率
- 内核功能:
- 时钟管理:实现计时功能
- 中断管理:负责实现中断机制
- 原语
- 对系统资源进行管理:进程管理、存储器管理、设备管理
- 其中,时钟管理、中断管理和原语是与硬件关联较为紧密的部分
- 原语:一种特殊的程序,处于操作系统最底层,是最接近硬件的部分。这种程序的运行具有原子性——其运行只能一气呵成,不可中断
3.2 进程控制
- 进程控制包括创建新进程、终止已完成的进程、进程的阻塞与唤醒等功能
- 系统为进程进行的操作:创建进程(分配内存、I/O、PCB);进程切换(保留现场、恢复环境);撤消进程(回收资源、撤消PCB)
- 进程控制一般是由OS内核中的原语实现控制,原语采用“开中断指令”和“关中断指令”实现
- 进程控制具体流程如下图:
3.2.1 进程的创建
- 引起创建进程的事件:用户登录、作业调度、提供服务(前三个由系统内核创建)、应用请求(由用户创建)
- 进程状态转换:无 -> 创建态 -> 就绪态
- 创建流程:(如下流程都是在 原语 中实现)
3.2.2 进程的终止
- 引起进程终止的事件:正常结束,异常结束,外界干扰
- 进程状态转换:就绪态/阻塞态/运行态 -> 终止态 -> 无
- 终止流程:
3.2.3 进程的阻塞与唤醒
- 引起进程阻塞与唤醒的事件:请求系统服务不能满足时 ;启动某种操作 如I/O;新数据尚未到达;无新工作可做
- 进程状态转换:
- 阻塞:运行态 -> 阻塞态
- 唤醒:阻塞态 -> 唤醒态
- 阻塞原语与唤醒原语必须成对出现
- 阻塞过程:
- 唤醒过程:
四、进程同步
- 进程同步定义:同步又称直接制约关系,它是指为完成某种任务而建立的两个或多个进程,这些进程因为需要在某些位置上协调他们的工作次序而产生的制约关系
- 两种形式的制约关系
- 间接相互制约关系:进程间要通过某种中介发生联系。即互斥关系,排他性地对资源的访问。
互斥必须满足两个条件:
(1)多个进程共享同一个临界资源。
(2)共享的方式是先来者先使用的异步方式。
- 直接相互制约关系:即同步关系,指多个进程的执行有先后顺序的限制。
- 临界资源: 系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源。如打印机、共享的变量、缓冲区等。
- 临界区(互斥区):在进程中访问临界资源的程序段叫临界区。
- 一个访问临界资源的循环进程应为如下结构:
While (1)
{
entry section;
critical section;
exit section;
remainder section;
};
其中:
1、进入区:进入区的代码用来检查临界资源是否正在被其它进程使用,若正在被访问,则不能进入临界区,若未被访问,则可进入临界区,并设置访问标志为真。
2、退出区:将临界区正在被访问的标志设置为假
- 同步机制应遵循的准则
- 为了保证进程互斥进入临界区,系统需设置专门的同步机制,同步机制应遵循4个准则:
- 1空闲让进。当无进程在临界区时,任何有权使用临界区的进程可进入。
- 2忙则等待。不允许两个以上的进程同时进入临界区。
- 3有限等待。任何进入临界区的进程应在有限的时间内得到满足。
- 4让权等待。当进程不能进入临界区时,应立即放弃CPU,以免进程陷入“忙等”。
4.1 信号量机制
- 信号量其实就是一个变量,可以用一个信号量来表示系统中某种资源的数量
- 一对原语:包含wait(S)原语和signal(S)原语,简称P、V操作,通常使用这一对原语对信号量S实施操作。该P、V操作必须成对出现
- 整型信号量
void wait(int S){
while(S <= 0);
S = S - 1;
}
void signal(int S){
S = S + 1;
}
注意:注意:在wait操作中,当S≤0时,就会不断测试而使进程处于“忙等”状态。不满足“让权等待”原则。
- 记录型信号量,可以实现让权等待原则。
typedef struct{
int value;
struct process *L;
}semaphore;
void wait(semaphore S){
S.value--;
if(S.value < 0){
block(S.L);
}
}
void signal(semaphore S){
S.value++;
if(S.value <= 0){
wakeup(S.L);
}
}
- AND型信号量
- 实现思想:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源,也不分配给他。
- 目的:避免死锁
Swait(S1, S2, …, Sn)
if S1≥1 and … and Sn≥1 then
for i∶=1 to n do
Si∶=Si-1;
endfor
else
把此执行进程插入第一个小于1的信号量Si的等待队列中,阻塞此执行进程;
endif
Ssignal(S1, S2, …, Sn)
for i∶=1 to n do
Si=Si+1;
把等待资源Si的队列中的所有进程移到就绪队列中;
endfor;
- 信号量集
Swait(S1, t1, d1, …, Sn, tn, dn)
if S1≥t1 and … and Sn≥tn then
for i∶=1 to n do
Si∶=Si-di;
endfor
else
把此执行进程插入第一个Si<ti的信号量Si的等待队列中,阻塞此执行进程;
endif
signal(S1, d1, …, Sn, dn)
for i∶=1 to n do
Si∶=Si+di;
把等待资源Si的队列中的所有进程移到就绪队列中;
endfor;
-
信号量集的几种情况:
- (1) Swait(S, d, d)。 此时在信号量集中只有一个信号量S, 但允许它每次申请d个资源,当现有资源数少于d时,不予分配
- (2) Swait(S, 1, 1)。 此时的信号量集已蜕化为一般的记录型信号量(S>1时)或互斥信号量(S=1时)。
- (3) Swait(S, 1, 0)。这是一种很特殊且很有用的信号量操作。当S≥1时,允许多个进程进入某特定区;当S变为0后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。
-
信号量机制实现进程互斥
- 分析并发进程的关键活动,划定临界区
- 设置互斥信号量mutex(semaphore类型)
- 在临界区之前执行P操作;临界区之后执行V操作
-
信号量机制实现进程同步
- 分析什么地方需要同步关系,即必须保证“一前一后”执行的两个操作
- 设置同步信号量S,初始为0
- 在“前操作”之后执行V操作;在“后操作”之前执行P操作
-
信号量机制实现前驱关系(实际上也是进程同步问题)
Var a,b,c,d,e,f,g; semaphore∶=0,0,0,0,0,0,0;
begin
parbegin
begin S1; signal(a); signal(b); end;
begin wait(a); S2; signal(c); signal(d); end;
begin wait(b); S3; signal(e); end;
begin wait(c); S4; signal(f); end;
begin wait(d); S5; signal(g); end;
begin wait(e); wait(f); wait(g); S6; end;
parend
end
*4.2 管程
- 背景:使用信号量机制,要求每个访问临界资源的进程都必须自备同步操作wait(s)和signal(s),使大量的同步操作分散在各个进程中,1) 系统无法有效控制、管理;2)容易导致死锁;3)不利于修改和维护
- 管程的定义:在程序设计级控制进程互斥与同步的机制
- 组成:① 局部于管程的共享变量说明;② 对该数据结构进行操作的一组过程;③ 对局部于管程的数据设置初始值的语句;④管程有一个名字(和类的概念类似)
- 管程使用的注意事项:
①局部于管程的数据结构,只能被局部于管程内的过程访问。
②局部于管程的过程只能访问管程内的数据结构。
③管程每次只允许一个进程进入,从而实现进程互斥。
五、经典进程的同步问题
同步问题的分析步骤
① 关系分析:找出题目中的各个进程,分析它们之间的同步、异步关系
② 整理思路:根据各进程的操作流程确定P、V操作的大致顺序
③ 设置信号量:根据题目条件确定信号量初值(互斥一般为1,同步要根据资源的初始值)
5.1 生产者-消费者问题
-
问题描述:1个生产者生产数据后写入缓冲区Buffer,1个消费者从缓冲区读出数据后消费。
-
关系分析:
- 同步关系:
当Buffer为满时,生产者进程必须等待消费者进程先执行;
当Buffer为空时,消费者进程必须等待生产者进程先执行。
- 互斥关系:
缓冲区是临界资源,各进程必须互斥的访问
-
整理思路
生产者消耗(P)一个缓冲区,同时生产(V)一个产品;
消费者消耗(V)一个产品同时释放(P)一个缓冲区
取走/放入产品需要互斥
-
设置信号量
semaphone mutex = 1; //互斥信号量,实现对缓冲区的互斥访问
semaphone empty = n; //同步信号量,表示空闲缓冲区的数量
semaphone full = 0; //同步信号量,表示产品的数量
-
实现
semaphore full=0, empty=N, mutex=1;
Producer (){
while(true) {
生产数据;
P(empty);
P(mutex);
写数据到Buffer;
V(mutex);
V(full);
}
};
Consumer (){
while(true) {
P(full);
P(mutex);
从缓冲区读数据;
V(mutex);
V(empty);
消费数据;
}
};
注意:上述两个P操作不可以改变顺序。假设现在缓冲区已满,即empty=0,full = n,此时先执行P(mutex)操作,进入临界区;再执行P(empty)操作,由于empty=0,因此该进程会被阻塞,此时若执行消费者进程,由于mutex=0,因此消费者进程也会被阻塞,这样会造成死锁的产生。因此,实现互斥的P操作一定要在实现同步的P操作之后,V操作的顺序是可以互换的
- AND实现
semaphore mutex=1, empty=N, full=0;
Producer i:
while(true) {
生产数据;
Swait(empty, mutex);
写数据到Buffer;
Ssignal(mutex, full);
};
Consumer i:
while(true) {
Swait(full, mutex);
从缓冲区读数据;
Ssignal(mutex, empty);
消费数据;
};
5.2 多生产者-多消费者问题
-
问题描述:桌子上一个盘子,每次只能放一个水果。爸爸专门像盘子中放入苹果,妈妈放入橘子。儿子专等橘子,而女儿专等苹果。只有盘子为空时,爸爸或妈妈才能放水果。仅当盘子中有自己需要的水果时,儿子或女儿才可以取出水果
-
关系分析:
- 互斥关系:
对盘子的访问要互斥进行
- 同步关系:
只有父亲放入苹果女儿才能取;
只有母亲放入橘子儿子才能取;
只有盘子为空时父母才可以放水果
-
整理思路
略
-
设置信号量
semaphone mutex = 1; //互斥信号量,实现对盘子的互斥访问
semaphone apple = 0; //同步信号量
semaphone orange = 0; //同步信号量
semaphone plate = 1; //同步信号量,表示还可以放多少个水果
-
实现
-
补充:即使不设置互斥变量mutex,也不会出现多个进程同时访问盘子的现象。因为在任何时刻,apple、orange、plate三个同步信号量最多只有一个是1,因此任何时刻都是最多只有一个进程的p操作不会被阻塞顺利进入临界区。(要是plate≠1那就要设置互斥变量了)
5.3 吸烟问题
-
问题描述:一个系统有三个抽烟者进程和一个供应者进程,抽烟者需要烟草、纸和胶水三种材料才能抽的了烟。现在三个抽烟者分别拥有其中一种材料,供应者每次将其中两种材料放入到桌子上,拥有剩下那种材料的抽烟者取走抽烟,并向供应者返回一个已抽完的信号,供应者就会再次放入两种材料到桌子上。一直重复上述过程,三个抽烟者轮流抽烟
-
关系分析:
- 互斥关系:
桌子需要互斥访问
- 同步关系:
供应者放入组合一,一号抽烟者才能抽烟
供应者放入组合二,二号抽烟者才能抽烟
供应者放入组合三,三号抽烟者才能抽烟
抽烟者返回抽完信号供应者才能再放材料
-
整理思路
略
-
设置信号量
略
-
实现
-
上述问题也无需设置互斥信号量,理由同上一个问题
5.4 读写问题
-
问题描述:对于文件、数据,可能有多个读者和写者对其进行操作。要求:多个读者可以同时操作;读者与写者、多个写者之间的操作应互斥。
-
关系分析:
- 互斥关系:
写者——写者;写者——读者
- 关键问题:读者——读者(虽然可以多个读者一起访问,但是若是多个读者同时访问可能会造成死锁问题,因此要对读者进行互斥进入访问)
-
整理思路
略
-
设置信号量
semaphore rmutex = 1; //读者对文件进行互斥访问
int count = 0; //记录当前有几个读者正在访问进程
semaphore wmutex = 1; //写者对文件进行互斥访问
-
实现
semaphore rmutex=1, wmutex=1; Readcount=0;
Writer()
{
while(1){
P(wmutex);
写数据;
V(wmutex);
}
}
Reader()
{
while(1){
P(rmutex);
if readcount=0 then P(wmutex);
Readcount=Readcount+1;
V(rmutex);
…
读数据;
…
P(rmutex);
readcount=readcount-1;
if readcount=0 then V(wmutex);
V(rmutex);
}
}
上述方法称为“读者优先”,即一旦有读者正在读数据,允许多个读者同时进入读数据,只有当全部读者退出,才允许写者进入写数据。
若实现写者优先,则需再加一个信号量 w = 1
semaphore rmutex=1, wmutex=1; Readcount=0;
Writer()
{
while(1){
P(w);
P(wmutex);
写数据;
V(wmutex);
V(w);
}
}
Reader()
{
while(1){
P(w);
P(rmutex);
if readcount=0 then P(wmutex);
Readcount=Readcount+1;
V(rmutex);
V(w);
…
读数据;
…
P(rmutex);
readcount=readcount-1;
if readcount=0 then V(wmutex);
V(rmutex);
}
}
5.5 哲学家问题
-
问题描述:五个哲学家同座一张圆桌,每人一个碗,左右各一只筷子;其习惯为:思考-吃饭-思考…;只有拿到左右两只筷子才开始吃饭,吃完后继续思考…。
-
关系分析:
- 互斥关系:
5个哲学家与其左邻右舍对其中的筷子的访问是互斥关系
-
整理思路
略
-
设置信号量
为了实现对筷子的互斥使用,可以用一个互斥信号量表示一只筷子,由这五个信号量构成信号量数组,即semaphore chopstick[5]={1,1,1,1,1};
-
实现
philosopher (i)
{
while (true) {
思考;
P(chopstick[i]) ;
P(chopstick[(i+1) mod 5]) ;
进食;
V(chopstick[i]) ;
V(chopstick[(i+1) mod 5]) ;
}
}
AND信号解决
philosopher (i) {
while (true) {
思考;
Sswait(chopstick[(i+1) mod 5], chopstick [i]);
进食;
Ssignal(chopstick [(i+1) mod 5], chopstick [i]);
}
}
- 若五个哲学家并发的拿起左边的筷子,则会导致死锁问题,解决办法如下:
(1) 至多只允许有四位哲学家同时去拿左边的筷子。
(2) 仅当哲学家的左、右两只筷子均可用时,才允许他拿起筷子进餐。
(3) 规定奇数号哲学家先拿他左边的筷子,然后再去拿右边的筷子;而偶数号哲学家则相反。
semaphore mutex = 1;
philosopher (i)
{
while (true) {
思考;
P(mutex);
P(chopstick[i]) ;
P(chopstick[(i+1) mod 5]) ;
V(mutex);
进食;
V(chopstick[i]) ;
V(chopstick[(i+1) mod 5]) ;
}
}
六、进程通信
- 定义:是指进程之间的信息交换。
- 进程通信分类:
- 低级通信:
特点:交换的信息量少,仅仅是一些数据和状态的变化;通信由程序员完成。如P,V原语实现的进程互斥与同步。
- 高级通信;
特点:每次交换的信息量可以很大;系统提供高效、简捷的信息传输命令。
- 进程通信的类型:
- 共享存储
- 基于共享数据结构的通信方式:公用数据结构的设置及对进程间同步的管理,都是由程序员完成,效率低,传递数据量少;
- 共享存储区的通信方式 (如windows的剪贴板):进程可随时向系统申请一块存储区, 并指定该区的关键字,用于进程通信。
- 消息传递
- 进程间的数据交换以格式化的消息为单位,通过OS提供的“发送消息/接收消息”两个原语进行数据交换
- 消息组成:消息头+消息体
- 消息传递方式:直接通信方式(将消息直接挂到缓冲队列上)、间接通信方式 (消息发送到中间实体中进行暂存)
- 管道通信
七、线程的基本概念
- 线程的引入:
- 操作系统引入进程的目的是为了使多个程序能并发执行,提高资源利用率和系统的吞吐量。
- 引入线程的目的是为了减少程序并发执行时所付出的时空开销,使OS具有更好的并发性。
- 线程的属性:
- 轻型实体:线程基本上不拥有系统资源,只有一点必不可少的、能保证独立运行的资源。
- 独立调度和分派的基本单位。
- 可并发执行:一个进程的多个线程可以并发;不同进程中的线程也能并发。
- 共享进程资源:同一进程中的各个线程可以共享该进程拥有的资源。
- 线程的定义:作为调度和分派的基本单位
- 引入线程的目的:将进程的资源申请和调度属性分开,即进程作为资源的申请和拥有者,但不作为调度的基本单位。使得进程内部各线程之间也可以并发
- 线程的状态:
① 执行状态,表示线程正获得处理机而运行;
② 就绪状态, 指线程已具备了各种执行条件,一旦获得CPU便可执行的状态;
③ 阻塞状态,指线程在执行中因某事件而受阻,处于暂停执行时的状态。
- 线程控制块TCB:在内核空间还为每一个内核支持线程设置了一个线程控制块, 内核是根据该控制块而感知某线程的存在的,并对其加以控制
- 多线程OS中的进程有以下属性:
(1) 作为系统资源分配的单位。
(2) 可包括多个线程(至少一个),在OS中的所有线程都只能属于某一个特定的进程。
(3) 进程不是一个可执行的实体。所谓进程处于“执行”状态,实际上是该进程的某线程正在执行。
- 内核支持线程 :每个线程的线程控制块设置在内核中,所有对线程的操作(创建、撤消和切换等),都是通过系统调用进入内核,再依靠内核中的相应处理程序予以实现的。
- 用户级线程:用户级线程仅存在于用户空间中,即每个线程的线程控制块设置在用户空间中,所有对线程的操作也是在用户空间中完成的,无须内核的支持。内核完全不知道用户级线程的存在。
- 二者的区别:对于设置了用户级线程的系统,调度仍以进程为单位进行。例若进程A中包含1个用户级线程,进程B包含100个用户级线程,用轮转法调度,则A中线程的运行时间,将是B中线程运行时间的100倍;假如系统设置的是内核支持线程,则调度便是以线程为单位进行。例若进程A中包含1个内核支持线程,进程B包含100个内核支持线程,用轮转法调度,则进程B可以获得的CPU时间是进程A的100倍。
八、调度的基本概念
- 定义:当有一堆任务要处理,由于资源有限,需要确定某种规则来决定处理这些任务的顺序,而确定的这些规则就称为调度。
- 处理机调度定义:即从就绪队列中按照一定的算法选择一个进程并将处理机分配给它运行,以实现进程的并发执行。
- 调度的三个层次
- 高级调度
定义:按照一定的原则从外存中处于后备队列的作业中挑选一个(或多个)作业,给他们分配内存等必要资源,并建立相应的进程(建立PCB),然后将新创建的进程排在就绪队列上,准备执行,又称作业调度。在每次执行作业调度时,都须做出以下两个决定:
- 接纳多少个作业
- 接纳哪些作业
- 中级调度(引入中级调度的主要目的:为了提高内存利用率和系统吞吐量。)
定义:即将暂时不能运行的进程调至外存上去等待(挂起状态),当这些进程又具备运行条件、且内存又稍有空闲时,由中级调度来把外存上的进程从挂起队列中调入内存,并修改其状态为就绪状态,挂在就绪队列上等待进程调度,又称内存调度。
中级调度即存储器管理中的对换功能。
- 低级调度
- 定义:也称进程调度,按照某种方法和策略从就绪队列中选取一个进程,将处理机分配给它,主要用来决定就绪队列中的哪个进程应获得处理机。
- 调用方式:采用以下两种调度方式:
- 非抢占方式:也称非剥夺调度方式。在该调度方式下,当进程分配到处理机时,其他进程不可以抢占,只有在进程自动放弃处理机时,才进行调度。
- 抢占方式:又称剥夺调度方式。当一个进程正在处理机上执行时,若有一个更重要的进程需要使用处理器,则立即暂停正在执行的进程,将处理机分配给更重要的进程。
- 抢占的原则:优先权原则,允许优先权高的新到进程抢占当前进程的处理机;短作业(进程)优先原则,短作业(进程)可以抢占当前较长作业(进程)的处理机;时间片原则,时间片用完则重新进行调度
|
调度发生在… |
对进程状态的影响 |
高级调度(作业调度) |
外存->内存(面向作业) |
无->创建态->就绪态 |
中级调度(内存调度) |
外存->内存(面向进程) |
挂起态->就绪态(阻塞挂起->阻塞态) |
低级调度 (进程调度) |
内存->CPU |
就绪态->运行态 |
补充知识:进程的七状态摩模型
8.1 计算参数
-
CPU利用率:指CPU“忙碌”的时间占总时间的比例
-
系统吞吐量:单位时间内完成作业的数量
- 系统吞吐量 = 总共完成了多少道作业/总共花了多少时间
-
周转时间:值从作业被提交给系统开始,到作业完成为止的这段时间。包括四部分:
a. 作业在外存后备队列上等待(作业)调度的时间。
b、进程在就绪队列上等待进程调度的时间。
c、进程在CPU上执行的时间。
d、进程等待I/O操作完成的时间。
其中bcd可能发生多次
周转时间 = 作业完成时间 - 作业提交(到达)时间
-
平均周转时间 = 各作业周转时间之和/作业数
-
带权周转时间 = 作业周转时间 / 作业实际运行的时间(恒>= 1)
-
平均带权周转时间 = 各作业带权周转时间之和 / 作业数
-
等待时间:指作业/进程处于等待处理机状态时间之和,等待时间越长用户满意度越低。
-
响应时间:指从用户提交请求到首次产生响应所用的时间
九、常见调度算法
9.1 先来先服务 FCFS
- 算法思想:类似与排队付款
- 算法规则:按照作业/进程到达的先后顺序进行服务
- 适用范围:用于作业调度,考虑的是哪个作业先到达后备队列;用于进程调度,考虑的是哪个进程先到达就绪队列
- 是否可抢占?:非抢占式算法
- 优点:公平、算法易实现
- 缺点:对长作业有利,对短作业不利
- 是否会导致饥饿:不会 (饥饿:某进程/作业长期得不到服务)
9.2 短作业优先 SJF
- 算法思想:最求最少的平均等待时间,最少的平均周转时间、最少的平均带权周转时间
- 算法规则:最短的作业/进程优先得到服务
- 适用范围:可用于作业调度;也可用于进程调度(SPF算法)
- 是否可抢占?:非抢占式算法、也有抢占式算法
- 优点:略
- 缺点:对短作业有利,对长作业不利
- 是否会导致饥饿:会 ,若不断有短作业进程到来,长作业可能会一直得不到服务,从而产生“饥饿”
9.3 高响应比优先 HRRN
- 算法思想:综合考虑作业/进程的等待时间和要求服务的时间
- 算法规则:在每次调度时先计算各个作业/进程的响应比,选择响应比最高的作业/进程为其服务
响应比 = ( 等待时间+要求服务时间 ) / 要求服务时间
- 适用范围:可用于作业调度;也可用于进程调度
- 是否可抢占?:非抢占式算法
- 优点:略
- 缺点:略
- 是否会导致饥饿:不会
9.4 时间片轮转 RR
- 算法思想:公平地、轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到相应
- 算法规则:按照各进程到达就绪队列的顺序,轮流让各个进程执行一个时间片。若进程未在一个时间片内执行完则剥夺处理机,将进程重新放到就绪队列队尾重新排队。一般默认新到达地进程先进入就绪队列。
- 适用范围:只可用于进程调度
- 是否可抢占?:抢占式算法,但是若一个进程未执行完一个时间片下一个进程不会抢夺处理机
- 优点:略
- 缺点:略
- 是否会导致饥饿:不会
- 时间片太大或太小分别有什么影响?
9.5 优先级调度算法
- 算法思想:根据任务的紧急程度来决定处理顺序
- 算法规则:调度时选择优先级最高的作业/进程
- 适用范围:可用于作业调度;也可用于进程调度
- 是否可抢占?:非抢占式抢占式都可
- 优点:略
- 缺点:略
- 是否会导致饥饿:会
- 优先级地分类:
- 静态优先级:创建进程时确定,之后一直不变
- 动态优先级:创建进程时有一个初始值,之后会根据情况动态地调整优先级
- 常见优先级:系统进程优先级高于用户进程 ; 前台进程优先级高于后台进程
- 确定进程优先权的依据有如下三个方面:(1)进程类型;(2) 进程对资源的需求;(3) 用户要求
9.6 多级反馈队列调度算法
- 算法思想:对其他调度算法的折中权衡
- 算法规则:
①设置多级就绪队列,各级队列优先级从高到低,时间片从小到大
②新进程到达时先进入第一级队列,按照FCFS原则排队等待被分配时间片,若用完时间片进程还未结束,则进程进入下一级队列队尾;若此时已经是在最下级的队列,则重新放回该队列队尾
③只有第k级队列为空时,才会为k+1级对头的进程分配时间片
- 适用范围:用于进程调度
- 是否可抢占?:抢占式算法,在k级队列的进程运行过程中,若更上级的队列中进入了一个新进程,该新进程便会抢占处理机,而原来运行的进程放回到k级队列中的队尾。
- 优点:略
- 缺点:略
- 是否会导致饥饿:会
十、死锁
10.1 死锁概述
-
定义:各个进程互相等待对方手里的资源,导致各进程都阻塞,无法向前推进的现象
-
进程死锁:一组进程中,每个进程都无限等待被该组进程中另一进程所占有的资源,因而永远无法得到的资源,这种现象称为进程死锁
-
产生死锁的原因:一、竞争资源引起的死锁;二、进程推进顺序不当引起的死锁
-
死锁产生的必要条件:(产生死锁必须同时满足以下四个条件)
- 互斥条件:一个资源每次只能给一个进程使用
- 不剥夺条件:一个进程在申请新的资源的同时保持对原有资源的占有
- 请求和保持条件:资源申请者不能强行从资源占有者手中夺取资源,资源只能由占有者自愿释放
- 循环等待条件:在出现死锁的系统中,一定存在一个进程—资源的环行链
注意:发生死锁时一定有循环等待,但是发生循环等待时未必发生死锁
-
处理死锁的基本方法:
- 不让死锁发生
- (1)预防死锁。 设置限制条件,破坏产生死锁的四个必要条件中的一个或几个条件。
- (2) 避免死锁。不设置限制条件,而是在资源分配过程中,防止系统进入不安全状态。
- 让死锁发生
- (1) 检测死锁。 允许死锁,并检测和清除死锁。
- (2) 解除死锁。 是与检测死锁相配套的一种措施。
10.2 预防死锁
做法:破坏死锁产生的四个必要条件之一或几个
- 互斥条件:无法破坏
- 不剥夺条件:规定进程逐个申请资源,当提出新的资源请求而不能立即得到满足时,必须释放已经保持的所有资源。待以后需要时重新申请。
- 请求和保持条件:采用静态分配方法,规定所有进程在开始运行之前,都必须一次性地申请其在整个运行过程中所需的全部资源。
- 循环等待条件: 把系统中所有资源编号,进程在申请资源时必须按资源编号的递增次序进行,否则操作系统不予分配。原因:总有一个进程占拒了较高序号的资源,则继续申请的资源必然空闲,因此进程可以一直向前推进。
10.3 避免死锁
- 做法:用某种方法防止系统进入不安全状态,从而避免死锁
- 安全状态:如果系统能按某种进程顺序(如P1,…,Pn, 称为安全序列)为每个进程分配其所需的资源,直至每个进程都能顺利地完成,称系统处于安全状态,成这个顺序为安全序列。安全序列可能有多个。
- 不安全状态:若不存在上述这样一个安全序列称系统处于不安全状态。
- 安全状态是一定没有死锁发生的。不安全状态不一定导致死锁发生
10.4 银行家算法
-
银行家算法中的数据结构
- (1) 可利用资源向量Available:它是一个含有m个元素的数组,其中每个元素代表一类当前可利用资源的数目
- (2) 最大需求矩阵Max。 n*m矩阵,表示n个进程的每一个对m类资源的最大需求。
- (3) 分配矩阵Allocation 。n*m矩阵,表示每个进程已分配的资源数。
- (4) 需求矩阵Need 。n*m矩阵,表示每个进程还需要各类资源数。
-
算法思想
- 设Requesti是进程的请求向量,Requesti[j]= K表示进程 Pi 需要 K 个 Rj 类型的资源。
- 当进程pi提出资源申请时,系统执行下列步骤:
- (1) 如果Requesti[j]≤Need[i,j],便转向步骤(2);否则认为出错(需要的资源数超过它所宣布的最大值)。
- (2) 如果Requesti[j]≤Available[j],便转向步骤(3);否则Pi须等待(无足够资源)。
- (3) 系统试着把资源分配给进程Pi,并修改下面数据结构中的数值:
Available[j]= Available[j]-Requesti[j];
Allocation[i,j]= Allocation[i,j]+Requesti[j];
Need[i,j]= Need[i,j]-Requesti[j];
- (4) 系统执行安全性算法,若系统新状态是安全的,则分配完成,若系统新状态是不安全的,则恢复原状态,进程等待
10.5 安全性算法
算法思想:
(1) 设置两个向量:
- ① 工作向量Work: 表示系统可提供给进程继续运行所需的各类资源数目,它含有m个元素,在执行安全算法开始时,Work = Available;
- ② Finish: 表示系统是否有足够的资源分配给进程,使之运行完成。开始时先做Finish[i]=false; 当有足够资源分配给进程时, 再令Finish[i]=true。
(2) 从进程集合中找到一个能满足下述条件的进程:
- ① Finish[i]=false; ② Need[i,j]≤Work[j]; 若找到, 执行步骤(3), 否则,执行步骤(4)。
(3) 当进程Pi获得资源后,可顺利执行,直至完成,并释放出分配给它的资源,故应执行:
- Work[j]∶= Work[ j ]+Allocation[i,j];
- Finish[i]∶= true;
- go to step 2;
(4) 如果所有进程的Finish[i]=true都满足, 则表示系统处于安全状态;否则,系统处于不安全状态。
10.6 死锁的检测与解除
1、做法:允许死锁的发生,OS负责检测出死锁的发生,然后采取某种措施接触死锁
2、判断死锁的方法:①用某种数据结构来保存资源的亲求和分配信息;②提供一种算法,利用上述信息来检测系统是否已进入死锁状态
3、资源分配图:
- 组成:结点:进程结点(对应一个进程);资源结点(对应一类资源);边:进程结点->资源阶段(表示进程想申请几个资源);资源节点->进程结点(表示已经为进程分配了几个资源)
- 检测死锁方法(资源分配图化简):
1)找一个既不阻塞又非独立的进程结点Pi(即找出一条有向边与它相连,且该有向边对应的资源的申请数量小于等于系统中已有空闲资源的数量)。释放其占有的全部资源,成为孤立结点。
2)再把相应的资源分配给一个等待该资源的进程。
3)重复以上步骤,若所有进程成为孤立结点,称该图是可完全简化的,否则称该图是不可完全简化的,就说明发生了死锁。
4、死锁状态的充分条件是:当且仅当资源分配图是不可完全简化的(死锁定理)。
5、死锁的解除
(1) 剥夺资源。从其它进程剥夺足够数量的资源给死锁进程。
(2) 撤消进程。 使全部死锁进程都夭折掉或按某种顺序逐个撤消进程,直至有足够资源可用。