操作系统学习-练习题个人总结(五)
第三章 进程管理
一、第四节-进程同步-课堂测试
1、错题解析
-
在多进程的系统中,为了保证公共变量的完整性,各进程应互斥进入临界区,所谓临界区是指(一段程序)。
解析:临界资源:一次仅允许一个进程使用的资源称为临界资源;临界区:进程中访问临界资源的一段代码。
-
下面关于管程的说法,不正确的是(管程是一种系统调用)。
解析:管程比信号量更容易保证并行编程的正确性;管程是一种编程语言成分;管程是一种进程同步机制
-
关于管程与进程比较的论述中,正确的是(管程内定义的数据结构能够由多个进程间接访问,进程内定义的是私有数据结构)。
解析:管程作为操作系统或编程语言成分,与进程一样也具有生命周期,由创建而产生,由撤销而消亡(是语法范围,不能撤销创建,并没有生命周期);管程能被系统中所有的进程调用(管程能被欲使用共享资源的进程调用);管程和调用它的进程能够并行工作(并不,是顺序的)。
2、正确知识点总结
- 要实现两个进程互斥,设一个互斥信号量mutex,当mutex为0时,表示(有一个进程进入临界区)。
- 有一个信号量的初值为3,经过多次P、V操作后,当前值为-1,则表示等待进入临界区的进程数为(1)。//绝对值的数目就是等待数目
- 在使用信号量机制实现互斥时,互斥信号量的初值一般为(1)【表示临界资源数目】;而使用信号量机制实现同步时,同步信号量的初值一般为(0)。
- 设与某资源相关联的信号量初值为3,当前值为1,若M表示该资源的可用个数,N表示等待该资源的进程数,则M、N分别为(1;0)
- 利用禁止中断的方法来实现互斥,只能用于单处理器系统中。(√)
- 不存在忙等现象的机制是(利用记录型信号量实现进程间互斥)。
补充:
进程同步:并发进程在一些关键点上可能需要互相等待或互通消息,这种相互制约的等待或互通消息称为进程同步。
同步机制应遵循的原则:
空闲让进:其他进程均不处于临界区;
忙则等待:已有进程处于其临界区;
有限等待:等待进入临界区的进程不能"死等";
让权等待:不能进入临界区的进程,应释放CPU(如转换到阻塞状态)
进程同步机制:
-
利用硬件方法解决进程互斥问题(系统自带)
禁止中断:【缺点】可能增加系统风险;只能用于单处理机系统(一个进程只能禁止本CPU的中断)
TSL指令:检测并上锁指令;lock=false,通过TSL判断lock变量的值,False-空闲/True-忙碌;会忙等
Swap指令:交换两个字的内容;lock=false,key=true,使用swap交换两个变量的值,直到key判false;违背让权等待原则(占着CPU等待);会忙等 -
利用软件方法解决进程互斥问题(代码实现)
不正确的算法:
(1)设立一个公用整型变量turn,描述允许进入临界区的进程标识,假设初始化turn=0,表示首先轮到P0访问临界资源。【违背了空闲让进(P0使用完成后P1才可使用临界区,若要先让P1进程使用临界区,进程都会空循环)、让权等待原则(在P1使用临界区的时候P0空循环判断,占用CPU并不处于阻塞状态)】
(2)设立一个标志数组flag[2]:描述进程是否已在临界区,初值均为0(FALSE),表示进程都不在临界区。【违背了忙则等待(同时执行时flag值容易都置为1,即都使用临界区)、让权等待原则(空循环)】
(3)设立一个标志数组flag[2]:描述进程是否希望进入临界区,初值均为0(FALSE),表示进程都不希望进入临界区。【违背了空闲让进、有限等待、让权等待原则】
------有关于这类代码判断违背了哪些原则的题,使用时间片轮转,代码逐条执行Peterson算法:结合上述(1)(2)算法,在(2)的基础上引入turn互相谦让,可推广到N个进程之间的互斥操作。【违背让权等待原则,会空循环忙等】
面包店算法:
每个进程设置一个唯一的编号Pi(i=0…n-1);
boolean choosing[n]:表示进程是否正在取号,初值为False;
int number[n]:记录每个进程取到的号码,初值为0。 -
利用锁机制解决进程互斥问题:w=1忙碌;w=0空闲;使用了加锁lock,开锁unlock原语;(代码思路和“禁止中断”类似,但是更有保障,具体实现也是不一样的)
-
利用信号量机制解决进程同步与互斥问题
整型信号量:【重点在于临界资源的占有与释放】
初始化操作:非负整数–表示临界资源的使用数目;P原语操作:down()或wait()–申请临界资源的操作;V原语操作:up() 或signal()–释放临界资源的使用权【违背了让权等待原则】
wait( S)
{ while(S ≤ 0);//空循环
S--;
}
signal(S) { S++ ; }
记录型信号量:【简单说就是记录阻塞的进程,并利用value的值来控制睡眠与唤醒的动作】
value: 初始化为一个非负整数值,表示空闲资源总数--若为非负值表示当前的空闲资源数,若为负值其绝对值表示当前等待临界资源的进程个数(没有申请到而被阻塞),若为0表示无进程也无空闲资源。
L:初值为空
typedef struct{
int value;/*信号量的值*/
PCB * L; /*进程等待队列队首指针*/
}semaphore ;
semaphore *S;
wait(S)
{ S->value--;
if(S->value < 0)
then sleep(S->L);//去阻塞队列,睡眠等待
}
signal(S)
{ S->value++;
if(S->value ≤ 0)
then wakeup(S->L);//唤醒进程
}
信号量集机制:【只要有一类不能分配就都不分配】
一次可申请多类资源;每类资源可申请多个。
s1:进程要互斥共享的多类临界资源;t1:系统规定的分配下限值;d1:一个进程对各类资源的申请数量
Swait(S1,t1,d1,S2,t2,d2, ...,Sn,tn,dn) {
if (S1>=t1 && ... && Sn>=tn)
{
for (i=1;i<=n;i++)
{
Si=Si –di;
}
}else {
将当前进程阻塞到第一个Si<ti的信号量Si的阻塞队列中;
}
}
Ssignal( S1,d1,S2,d2, ...,Sn,dn)
{
for (i=1;i<=n;i++)
{
Si=Si + di;
唤醒所有因S1~Sn而阻塞的进程,插入就绪队列;
}
}
Swait(S,d,d):表示每次申请d个资源
Swait(S,1,1):表示记录型信号量
Swait(S,1,0):可作为一个可控开关
Swait(S1, 1,1,S2,1,1, …,Sn,1,1):表示AND型信号量
PS:一些经典信号量机制解决同步互斥问题的算法,详见本节最后小结。
管程:
//定义:一个管程定义了一个数据结构和能为并发进程所执行(在该数据结构上)的一组操作,这组操作能同步进程和改变管程中的数据。
组成:管程名字;局部于管程的共享变量的说明;对该数据结构进行操作的一组过程;初始化局部变量的语句。
特性:信息隐蔽性(外访内;内访内);任一时刻,管程中只能有一个活跃进程。
二、第四节-进程同步-课后作业
- 假设有三个并发进程A,B,C,其中A负责从输入设备上读入信息并传送给B,B将信息加工后传送给C,C负责将信息打印输出。进程A、B共享一个单缓冲区,进程B、C共享另一个单缓冲区。(一个单缓冲区中只能放一条信息)。请用记录型信号量实现三个进程间的同步算法。
empty1,empty2,ful1,full2:semaphore
main() {
empty1,empty2=1,1;
full1,full2=0,0;
parbegin(A,B,C);
}
void A () {
do{ get data;
wait(empty1);
put data to buffer1;
signal(full1);
}while(1)
}
void B () {
do{ wait(full1);
get data from buffer1;
signal(empty1);
wait(empty2);
put data to buffer2;
signal(full2);
}while(1)
}
void C () {
do{ wait(full2);
get data from buffer2;
signal(empty2);
}while(1)
}
- 桌子上有一个空盘子,最多允许存放两只水果,但一次只能一个人操作盘子(往盘子中放水果或从盘子中取水果),爸爸可以向盘中放苹果,妈妈向盘子中放橘子,女儿专门吃盘子中的苹果,儿子专门吃盘子中的橘子。请用信号量实现他们之间的同步与互斥关系。
S:semaphore=2;//盘子开始可以放两只水果
Mutex:semaphore=1;//互斥使用盘子
S1:semaphore=0;//是否有苹果
S2 :semaphore=0;//是否有橘子
Process Father:
Begin: L1: P(S);
P(mutex);
Put Apple;
V(mutex);
V(S1);
GO TO L1;
End;
Process Son:
Begin: L3: P(S2);
P(mutex);
Get Orange;
V(mutex);
V(S);
GO TO L1;
End;
Process Mother:
Begin:L2: P(S);
P(mutex);
Put Orange;
V(S2);
V(mutex);
GO TO L2;
End;
Process Daughter:
Begin:L4: P(S1);
P(mutex)
Get Apple;
V(mutex);
V(S);
GO TO L4;
End;
- 某工厂有一个可以存放设备的仓库,总共可以存放10台设备。生产的每一台设备都必须入库,销售部门可从仓库提取设备供应客户。设备的入库和出库都必须借助运输工具。现只有一台运输工具,每次只能运输一台设备。请设计一个能协调工作的自动调度管理系统。
semaphore empty,full,S;
void main(){
empty = 10;
full = 0;S = 1;
parbegin(in(),out());
}
in(){
do{
生产了一台设备;
P(empty);
P(S);
使用运输工具入库;
v(S);
v(full);
}while(1)
}
out(){
do{
P( full);
P( S);
使用运输工具出库;
V(S);
V( empty);
提供设备供应客户;
}while(1)
}
三、第四节-进程同步-经典算法小结
- 生产者-消费者问题
共享缓冲区-多个生产者进程、多个消费者进程
- 进程间的关系:
互斥:多个进程间互斥使用同一个缓冲池;
同步:当缓冲池空时,消费者必须阻塞等待;当缓冲池满时,生产者必须阻塞等待。 - 设置信号量:
Mutex:用于访问缓冲池时的互斥,初值是1
Full:“满缓冲”数目,初值为0;
Empty:“空缓冲"数目,初值为K。full+empty=K - 算法描述:
生产者:首先判断缓冲区是否有空,如果有空再判断缓冲区的是否正在使用,若空闲则进行生产操作。操作完成后,需要及时释放缓冲区的使用权,以及对缓冲区“满缓冲”的数目进行更改,提示消费者进程进行消费操作。 - 简易伪代码:
semaphore mutex = 1;//互斥信号量
semaphore empty = k;//缓冲区“空”数目
semaphore full = 0;//缓冲区“满”数目
program producer:
wait(empty);
wait(mutex);
buffer操作;
signal(mutex);
signal(full);
program consumer:
wait(full);
wait(mutex);
buffer操作;
signal(mutex);
signal(empty);
- 需要注意:代码中wait操作顺序不可更改;signal可以更改,但会占用资源,还是尽早释放较好。
- 哲学家进餐问题
- 问题描述:有五个哲学家坐在一张圆桌旁,在圆桌上有五个盘子有五只筷子,他们的生活方式就是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿时取其左右两只筷子,只有拿到这两只筷子时才能进餐;进餐完毕,放下筷子继续思考。
- 解决思路:
设置信号量:semaphore chopstick[0…4];
第一种:每个人都先拿左手边的筷子再拿右手边的筷子,都拿到之后开始进餐。【此种方法若五人同时拿筷子,就会产生死锁】
第二种:给每个哲学家以及筷子编号,奇数号哲学家拿右手边的筷子,偶数号哲学家拿左手边的筷子。
第三种:限制四个人进行第一种算法思路,保证至少有一个人能顺利进餐。附加设置信号量semaphore sm = 4限制人数为4。
program philosopher(i){
wait(sm);
wait(chopstick[i]);
wait(chopstick[(i+1)mod 5]);
eating;
signal(chopstick[i]);
signal(chopstick[(i+1)mod 5]);
signal(sm);
}
- 读者-写者问题
- 进程间的关系:
当有写者在写数据时,其他写者和读者必须等待;
当有读者在读数据时,其他写者必须等待;但其他读者可以同时读数据。 - 信号量设置:
信号量wmutex表示“允许写”,写者与其他进程互斥使用数据;
公共整形变量Readcount表示“正在读”的读者数;
信号量Rmutex:实现多个读者对Readcount的互斥操作; - 算法描述一(读者优先):
写者:判断是否允许写,若允许则写入内容,完成后释放互斥信号量。
读者:首先去判断rmutex是否被其他进程使用,若没有则进一步判断,使用if分支。
若为0,则表示当前无读者在读,故判断wmutex信号量是否被使用,即是否有写者在写,若没有则占用数据,及时增加读者数量后也需要及时释放对rmutex的使用权,方便其他读者读取;再进一步判断当前读者是否为最后一位读者,若为最后一位则可以释放wmutex信号量。首先获取rmutex的使用权,对读者数量减一,若减一后为0,则为最后一位,相应的释放wmutex信号量,否则只能释放rmutex信号量,结束此轮进程判断。 - 简易伪代码:
wmutex:semaphore = 1;//读者与写者之间、写者与写者之间互斥使用共享数据
readcount: int = 0;//当前正在读的读者数量
rmutex:semaphore = 1://多个读者互斥使用readcount
void writer{
while(1){
wait(wmutex);
writing...
signal(wmutex);
}
}
void reader{
while(1) {
wait(rmutex)
if readcount=0
then wait(wmutex);
readcount++;
signal(rmutex);
reading...
wait(rmutex);
readcount--;
if readcount=0
then signal(wmutex);
signal(rmutex);
}
}
- 算法描述二(写者优先)
- 简易伪代码:
semaphore wmutex = 1//读者与写者之间、写者与写者之间互斥使用共享数据
Semaphore S = 1//当至少有一个写者准备访问共享数据时,它可使后续的读者等待写完成
Semaphore S2 = 1//阻塞第二个以后的等待读者
int Readcount,writecount = 0,0;//当前读者数量、写者数量
Semaphore mutex1 = 1//多个读者互斥使用readcount
Semaphore mutex2 = 1//多个写者互斥使用writecount
Reader() {
while(1){
wait(S2);
wait(S);
wait(mutex1);
if readcount=0
then wait(wmutex);
readcount++;
signal(mutex1);
signal(S);
signal(S2);
reading...
wait(mutex1);
readcount--;
if readcount=0
then signal(wmutex);
signal(mutex1);
}
}
void writer() {
while(1)
wait(mutex2);
if writecount=0
then wait(S);
writecount++;
signal(mutex2);
wait(wmutex);
writing...
signal(wmutex);
wait(mutex2);
writecount--;
if writecount=0
then signal(S);
signal(mutex2);
}
}
- 理发师问题
- 信号量设置:
int waiting;表示坐在等候椅上的顾客数
Cust_ready:理发椅上的顾客数;阻塞理发师
Finished:顾客是否已完成理发
mutex:互斥访问waiting
chair:空闲的理发椅数量 - 算法描述:
理发师:首先判断是否有顾客在理发椅上等候,若有则进行理发,完成操作后提醒finished。
顾客:首先获取waiting的使用权,判断等候椅是否满了,若满了则释放waiting的使用权离开;若没满,则更改waiting的值,并释放使用权。之后判断理发椅是否被使用,若空闲则坐上理发椅,重复获取waiting的使用权并更改waiting的值,记得及时释放。再通过cust_ready释放信号告诉理发师可以理发,理发师进行操作后,等待理发师通过finished信号量告诉顾客理发完成,最后离开理发椅,及时释放理发椅的使用权。 - 简易伪代码:
int waiting;
semaphore cust_ready,finished,mutex,chair;
void main() {
waiting = 0;
cust_ready = finished = 0;
mutex = chair=1;
parbegin(barber,customer-i);
void barber() {
do {
wait(cust_ready);
cut_hair;
signal(finished);
}while(1);
}
void customer-i (){
wait(mutex);
if(waiting < n) {
waiting=waiting+1;
signal(mutex);
}else {
signal(mutex);
离开理发店;
return;
}
wait(chair);
sit_in_chair;
wait(mutex);
waiting=waiting-1;
signal(mutex);
signal(cust_ready);
get-haircut;
wait(finished);
stand_from_chair;
signal(chair);
}