操作系统(2.3进程同步)本章最后结合记录型信号量的使用方法和例题进行了详细讲解。

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JACK_JYH/article/details/81783999

最近在准备推免的面试把王道的进程这一章拿出来做了一下,收获挺多的,写个文章总结下

 2.3进程同步

访问临界资源过程

do{

entry section;//进入区 设置访问临界区标志

critical section;//临界区 访问临界资源

exit section;//退出区 将访问临界区标志清除

remainder section;//剩余区 

}while(true)

软件实现临界区互斥

1.单标志法

单标志法主要问题是对临界区资源的访问各个进程需要交替进行,如果一个进程完成后,及时临界区可以访问,另一个进程也无法进入。这就违背了“空闲让进”原则

P0进程:
while(turn!=0);
critical section;
turn=1;
remainder section;
p1进程:
while(turn!=1);
critical section;
turn =0;
remainder section;

2.双标志法先检查

先判断对方是否在访问临界区,然后再决定自己是否进入,它都问题是如果按照1 2 3 4的顺序执行的话,双法都进入临界区,这样就违背了忙则等待的原则

pi进程
while(flag[j));//1
flag[i]=TRUE;//3
critical section;
flag[i]=FALSE;
remainder section;

pj进程
while(flag[i]);//2
flag[j]=TRUE;//4
critical section;
flag[j]=FALSE;
remainder section;

3.双标志后检查法

先将临界区设置为自己的,再判断对方是否访问,这个方法的问题是如果按照1 2 3 4 执行的话,3,4 都会永远处于真,这样会导致饥饿问题

pi进程
flag[i]=TRUE;//1
while(flag[j]);//3
critical section;
flag[i]=FALSE;
remainder section;

pj进程
flag[j]=TRUE;//2
while(flag[i]);//4
critical section;
flag[j]=FALSE:
remainder section;

4.Peterson‘s Algorithm

该算法的核心是通过增加turn让后执行的turn赋值语句,决定谁先进入临界区

pi进程
flag[i]=TRUE;//1
turn=j;
while(flag[j]&&turn==j);//3
critical section;
flag[i]=FALSE;
remainder section;

pj进程
flag[j]=TRUE;//2
turn=i;
while(flag[i]&&turn==i);//4
critical section;
flag[j]=FALSE:
remainder section;


硬件实现方法

1.中断屏蔽方法

因为cpu只有在发生中断的时候才能进行进程切换,因此关中断就可以保证临界区资源顺利访问

2.硬件指令

TestAndSet指令

Swap 指令

优点:适用于任意数目的进程,不管是单处理机还是多处理机:简单,容易验证其正确性,可以支持进程内有多个临界区,只需为没给临界区设立一个信号量即可。

缺点:进程等待进入临界区时需要消耗处理机时间,不能实现让权等待,从等待进入进程中随机选择一个进入临界区,有的进程可能一直选不上,从而导致”饥饿“现象。

信号量

信号量只能被两个标准的原语wait(S)和signal(S)来访问,也可被记错P/V

1.整型信号量

一个用于表示资源数目的整型量S,wait和signal操作可描述为下方,问题是wait中如果S<=0,就会不断尝试,因此该机制并未遵循“让权等待”原则

wait(S){
    while(S<=0);
    S=S-1;
}
signal(S){

S=S+1;

}

2.记录型信号量

记录型信号量通过一个记录进程的队列来避免重复尝试。

记录型信号量数据结构

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);
    }
}

重要概念

1.临界资源:一次仅允许一个进程使用的资源。

2.临界区:进程中用于访问共享资源的代码。

3.临界资源是互斥共享资源,非共享数据不属于临界资源。

4.可重入代码(Reentry code)也叫纯代码(Pure code)是一种允许多个进程同时访问的代码。为了使各进程所执行的代码完全相同,故不允许任何进程对其进行修改

5.公用队列可供多个进程使用,但一次只可有一个进程使用。

6.P,V 操作是一种低级的进程通信原语,它是不能被中断的,也不是系统调用的。

7.执行P操作可能导致进程阻塞,但是进程在执行这个操作时还是执行状态。

8.用P,V操作实现进程同步,信号量的初值由用户确定。若期望的消息尚未产生,则置初值为 0,若期望的消息已经存在,则置为条件中的数值,比如读者写者问题,empty因为已经存在置为n,full开始时不存在就置为0。

9.管程不仅能实现进程间的互斥,而且能够实现进程间的同步。

10.原语是指完成某种功能且不被分割不被中断执行的操作序列,通常可由硬件来实现完成不被分割执行特性的功能。在单处理机时还可以通过软件关闭中断的方式。

问题

1.信号量的初始值问题?

信号量有两种资源信号量和互斥信号量,同步信号量初始值为1,小于0时,其绝对值为在等待进入该临界区的进程数。资源信号量初始值为该临界资源的数目,小于0时,其绝对值为在等待该临界资源的进程数,大于0时,其绝对值为该临界资源的剩余数。

2. 管程wait/signal与PV操作

信号量的P操作可能阻塞,也可能不阻塞;而管程的wait操作一定会阻塞。信号量的V操作如果唤醒了其他线程,当前线程与被唤醒线程并发执行;对于管程的signal操作,要么当前线程继续执行,要么被唤醒线程继续执行,二者不能并发。 同时信号量的V操作一定会改变信号量S的值(S=S+1)。管程中的signal操作是针对某个变量的,如果不存在因该条件而阻塞的进程,signal不会产生任何影响。

信号量的利用

1.信号量的同步

要求P1执行X语句后才可执行P2 Y语句,通过在将S=0,和在Y语句上方放置P操作,来限制P2执行,通过在P1 X语句后放置V操作来使P2可以继续执行,见代码。

semaphore S=0;
P1(){
    x;
    V(S);
}
P2(){
    P(S);
    y;
}

2.信号量的互斥

通过将S=1和在对临界区资源上方放置P操作来实现,因为一旦有个进程实现了P操作别的进程就P操作就会组织他们进入临界区只能等第一个进程的V操作

semaphore S=1;
P1(){
    P(S);
    临界区;
    V(S);
}

P2(){
    P(S);
    临界区;
    V(S);
}

3 信号量的前驱图(同步的加强版)

只需根据每个节点的出度入度即可得知P/V操作的数量,同时每条边都是一个同步关系需要设置一个信号量,出度是V操作,入度是P操作,参考案例

S1->S2  a1

S1->S3 a2

S2->S4 b1

S2->S5 b2

S3->S6 c

S4->S6 d

S5->S6 e

semaphore a1=a2=b1=b2=c=d=e=0;
S1(){
    ...;
    V(a1);V(a2);
}
S2(){
    P(a1);
    ...;
    V(b1);V(b2);
}

S3(){
    P(a2);
    ...;
    V(c);
}

S4(){
    P(b1);
    ...;
    V(d);
}

S5(){
    P(b2);
    ...;
    V(e);
}


S6(){
    P(c);
    P(d);
    P(e);
    ...;
}

学习了信号量的利用让我们将他们融汇贯通吧

经典同步问题

1.生成者消费者问题

问题描述

一组生产者进程和一个消费者进程共享一个初始为空,大小为n的缓冲区,只有缓冲区没满时,生产者才能把消息放入到缓冲区,否则必须等待;只有缓冲区不空时,消费者才能从中取出消息,否则必须等待。由于缓冲区是临界资源,它只允许一个生产者放入消息,或者一个消费者从中取出消息。

关系分析:生产者于消费者因为有先后顺序,所以是同步关系,因为对缓冲区的争夺所以也是互斥关系。

思路:设置一个mutex=1来实现对缓冲区的互斥反问,要知道缓冲区对于生产者的资源是空多少,他的初始值是empty=n,缓冲区对消费者的资源是满多少,顾设置full=0;

semaphore mutex=1;
semaphore empty=n;
semaphore full=0;
producer(){
    while(True){
        P(empty)
        P(mutex)
        put();
        V(mutex)
        V(full);
    }
} 
consumer(){
    while(True){
        P(full)
        P(mutex)
        put();
        V(mutex)
        V(empty);
    }
}    
     
    

注解:有的人可能会问为什么mutex要在full或者empty的里面,可以这么理解,他们各自都必须先确定自己可以对缓冲区操作才能占用缓冲区,否则会出现死锁,比如一开始对消费者如果mutex先锁定缓冲区,然后p(full)发现缓冲区为空被阻塞,但因为他对缓冲区的锁定使得生成者也没有机会进入,导致死锁。

生产者-消费者变种题1

问题描述:桌子上有一只盘子,每次只能向其中放入一个水果,爸爸专门向盘子中放入苹果,妈妈专门向盘子中放入橘子,儿子专等吃盘子中的橘子,女儿专等吃盘子中的苹果。只有盘子为空时,爸爸或妈妈就可向盘子中放一个苹果,仅当盘子中的有自己需要的水果时,儿子或女儿可以从盘子中取出。

关系分析:爸爸妈妈之间对盘子是互斥的,爸爸女儿和妈妈儿子是同步的

思路分析:设置mutex=1互斥访问盘子,设置emptyA=1,fullA=0,同步爸爸女儿,设置emptyB=1,fullB=0,同步妈妈儿子。

semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
    while(True){
        P(emptyA)
        P(mutex)
        putApple();
        V(fullA);
    }
}
Mom(){
    while(True){
        P(emptyB);
        P(mutex);
        putJuice();
        V(fullB);
    }
}
daughter(){
    while(True){
        P(fullA);
        getApple();
        V(mutex);
        V(emptyA);
    }
}
son(){
    while(True){
        P(fullB);
        getJuice();
        V(mutex);
        V(emptyB);
    }
}

上方为标准解法但是仔细想想是可以简化的,因为mutex等价于empty,因为如果获得盘子权力,那必是儿子,女儿已经拿掉了,通过V操作唤醒的,那盘子必定是空,可以放入无需检查,因此去掉两个empty仍然正确。

 semaphore mutex=1;
semaphore emptyA=1,fullA=0;
semaphore emptyB=1,fullB=0;
dad(){
    while(True){
        P(mutex)
        putApple();
        V(fullA);
    }
}
Mom(){
    while(True){
        P(mutex);
        putJuice();
        V(fullB);
    }
}
daughter(){
    while(True){
        P(fullA);
        getApple();
        V(mutex);
    
    }
}
son(){
    while(True){
        P(fullB);
        getJuice();
        V(mutex);
       
    }
}     

生产者-消费者变种问题2

(2009年计算机联考真题)三个进程P1,P2,P3互斥使用一个包含N个单元的缓冲区,P1每次用produce()生成一个正整数并用put()送入缓冲区某一空单元中;P2每次用getodd()从该缓冲区中取出一个奇数并用countodd()统计奇数个数;P3每次用geteven()从该缓冲区中取出一个偶数并使用counteven()统计偶数个数,请使用信号量机制实现三个进程的同步与互斥活动,并说明所定义的信号量的含义。

关系分析:P1与P2/P3是同步,他们之间因为对缓冲区的争夺是互斥

思路分析:设置mutex=1实现对缓冲区的互斥访问,设置empty=n和full=0来实现同步。

semaphore mutex=1;
semaphore empty=n;
semaphore pA=0;
semaphore pB=0;
P1(){
    while(True){
        P(empty);
        x=produce();
        P(mutex);
        put();
        V(mutex);
        if(x%2==0){
            V(pB)
        }else{
            V(pA);
        }
    }
}

P2(){
    while(True){
        P(pA);
        P(mutex);
        getodd();
        V(mutex);
        V(empty)
        countodd();
    }
}

P3(){
    while(True){
        P(pB);
        P(mutex);
        geteven();
        V(mutex);
        V(empty);
        counteven();
    }
}

生产者-消费者变种问题3

在一个仓库中可以存放A和B两种产品,要求:

(1)每次只能存入一种产品。

(2)A产品数量-B产品数量<M

  (3)   B产品数量-A产品数量<N

其中M,N是正整数,试用P操作,V操作描述产品A与产品B的入库过程。

思路分析:A最多能生产的数量肯定是M-1,B最多能生产的数量最多肯定是N-1,同时A生成

semaphore AB=M-1;
semaphore BA=N-1;
semaphore mutex=1;
P1(){
    while(True){
        P(AB)
        P(mutex)
        A();
        V(mutex)
        V(BA);
    }
}
P2(){
    while(True){
        P(BA)
        P(mutex)
        B();
        V(mutex)
        V(AB);
    }
}

生产者-消费者变种问题4

某个工厂有两个生产车间和一个装配车间,两个生产车间分别生产A,B两种零件,装配车间的任务是把A,B两种零件组装成产品。两个生产车间每生产一个零件后都要分别把它们送到装配车间F1,F2上,F1存放零件A,F2存放零件B,F1和F2的容量均可以存放10个零件。装配工人每次从货架上取出一个零件A和一个零件B后组装成产品。请用P,V操作进行正确管理

关系分析:装配车间为消费者,生产A,B两种零件的是车间是生产者。

思路分析:这道题唯一不同就是多了一个组装,骑士只要把获取两种产品的过程写在一起就是组装了,mutexA=1,mutexB=1,对车间的互斥访问,emptyA=10,emptyB=10,fullA=0,fullB=0。

semaphore emptyA=10,emptyB=10;
semaphore fullA=0,fullB=0;
semaphore mutexA=1,mutexB=1;
PA(){
    while(True){
        P(emptyA);
        P(mutexA);
        putA();
        V(mutexA);
        V(fullA);
    }
}
PB(){
    while(True){
        P(emptyB);
        P(mutexB);
        putB();
        V(mutexB);
        V(fullB);
    }
}
PC(){
    while(True){
        P(fullA);
        P(mutexA);
        getA();
        V(mutexA);
        V(emptyA);
        
        P(fullB);
        P(mutexB);
        getB();
        V(mutexB);
        V(emptyB);
        //组装
    }
}

生产者消费者变种问题5

某寺庙,有小和尚,老和尚若干,有一水缸,由小和尚提入水缸供老和尚饮用,水缸可容纳10桶水,水取自同一井中,水井径窄,每次只能容纳一桶取水,水桶总数为3个,每次入缸取水仅为1桶水,且不可同时进行。试给出有关从缸取水,入水的算法描述。

关系分析:小和尚和老和尚之间喝水为同步,同时对水缸为互斥,对桶进行争夺,小和尚之间对井的争夺也是互斥

思路分析:井互斥mutexJ=1;缸互斥mutexG=1,桶资源mutex=3;full=0,empty=10;

semaphore mutexJ=1,mutexG=1,Tong=3,full=0,empty=10;
Psmall(){
    while(True){
        P(empty);
        P(Tong)
        P(mutexJ);
        //取水
        P(mutexJ);
        P(mutexG);
        put();
        V(mutexG);
        V(full);
        V(Tong);
    }
}
Podd(){
    while(True){
        P(full);
        P(Tong);
        P(mutexG);
        get();
        V(mutexG);
        V(empty);
        //喝水
        V(Tong);
    }
}

生产者消费者变种问题6

设P,Q,R共享一个缓冲区(该缓冲区大小为1),P,Q构成一对生产者-消费者,R既为生产者又为消费者,使用P,V操作实现其同步。

思路分析:R既为消费者又为生产者,则必须在执行前判断状态,若empty=1,则执行生产者功能,若full==1,执行消费者功能

semaphore full=0;
semaphore empty=1;
semaphore mutex=1;
Procedure P
{
    while(TRUE){
        P(empty);
        P(mutex);
        put();
        V(mutex);
        V(full);
    }
}

Procedure Q
{
    while(TRUE){
        P(full);
        P(mutex);
        get();
        V(mutex);
        V(empty);
    }
}


Procedure R
{
    while(TRUE){
    if(empty==1){
        P(empty);
        P(mutex);
        put();
        V(mutex);
        V(full);
    }
    if(full==1){
        P(full);
        P(mutex);
        get();
        V(mutex);
        V(empty);
    }
}

生产者消费者变种问题7

设自行车生产线上有一只箱子,其中有N个位置(N>=3),若每个位置可存放一个车架或一个车轮,又设有三个工人,其活动分别为:

工人1活动:

do{

加工一个车架;

车架放入箱中;

}while(1)

工人2活动:

do{

加工一个车轮;

车架放入箱中;

}while(1)

工人3活动:

do{

箱中取出一个车架;

箱中取二个车轮;

组装为一台车

}while(1)

试分别用信号量宇PV操作实现三个工人的合作,要求解中不含死锁

思路分析:本体需要知道一个限制虽然成产线有N个位置但是车轮的最大数目却是N-1,车架是N-2,否则会出现死锁。同时需要设置一个mutex=1来互斥访问缓冲区,设置

semaphore mutex=1;
semaphore empty1=N-2;
semaphore full1=0;
semaphore empty2=N-1;
semaphore full2=0;
semaphore empty=N;
do{
    P(empty1);
    P(empty);
    加工一个车架;
    P(mutex);
    车架放入箱中;
    V(mutex);
    V(full1);

}while(1)

工人2活动:

do{
    P(empty2);
    P(empty);
    加工一个车轮;

    P(mutex);
    车轮放入箱中;
    V(mutex);
    V(full2);

}while(1)

工人3活动:

do{
    P(full1);
    P(mutex);
    箱中取出一个车架;
    V(mutex);
    V(empty);
    V(empty1);

    P(full2);
    P(full2);
    P(mutex);
    箱中取一个车轮;
    V(mutex);
    V(empty);
    V(empty);
    V(empty2);
    V(empty2);


    组装为一台车

}while(1)

注释:本体没有强制要求去拿互斥,可以去掉mutex

生产者消费者变种问题8

(2015年计算机联考题)有A,B两个人通过信箱进行辩论,每个人都从自己的信箱中国呢取得对方的问题。将答案和向对方提出的新问题组成一个邮件放入对方的邮箱中。假设A的信箱最多放M个邮件,B的信箱最多放N个邮件,初始时A的信箱中有x个邮箱(0<x<M),B的信箱中有y个(0<y<N),辩论者每次取出一个邮件,邮件数减1,A和B两个人的操作过程描述如下

CoBegin

A{

while(TRUE){

从A的信箱中取出一个邮件;

回答问题并提出一个新问题;

将新邮件放入B的信箱;

}

}

B{

while(TRUE){

从B的信箱中取出一个邮件;

回答问题并提出一个新问题;

将新邮件放入A的信箱;

}

}

当信箱不为空时,辩论者才能从信箱中取邮件,否则等待,当信箱不满时,辩论者才能将新邮件放入信箱,否则等待。请添加必要的信号量和P,V(或wait,signal)操作,以实现上述过程的同步,要求写出完整步骤。

思路分析:本题是个双向生产者消费者问题,同时要求每次要将获得的东西整理后重新发给对方。

semaphore empty1=N-x,full1=x;
semaphore empty2=M-y,full2=y;
semaphore mutex1=1,mutex2=1;
A{

    while(TRUE){
    V(full1);
    P(mutex1);
    从A的信箱中取出一个邮件;
    V(mutex1);
    P(empty2);
    
    回答问题并提出一个新问题;
    P(mutex2);
    将新邮件放入B的信箱;
    V(mutex2);

    }

}

B{

    while(TRUE){
    V(full2);

    P(mutex2);
    从B的信箱中取出一个邮件;
    V(mutex2);

    P(empty1);

    回答问题并提出一个新问题;

    P(mutex1);
    将新邮件放入A的信箱;
    V(mutex1);

    }

}

猜你喜欢

转载自blog.csdn.net/JACK_JYH/article/details/81783999