2.3.6生产者消费者问题,哲学家进餐问题,读者-写者问题

全知识整理目录

操作系统整理的目录,包括了整理的所有操作系统的知识。


目录

概述

 生产者-消费者问题

多生产者-多消费者问题

读者-写者问题

吸烟者问题

哲学家进餐问题


概述

如何使用信号量机制(P,V操作)实现生产者,消费者进程的功能呢?

同步:设置初值为0的同步信号量。

互斥:设置初值为1的互斥信号量。

对资源的申请和释放:设置一个信号量,初始值为资源的数量,需要等待其他进程释放资源才能继续执行下去。

 生产者-消费者问题

系统中有一组生产者进程,一组消费者进程,生产者进程每次生产一个产品放入缓冲区,消费者进程每次从缓冲区中取出一个产品使用。

  • 生产者消费者共享一个初始为空,大小为n的缓冲区。
  • 缓冲区是临界资源,各进程必须互斥访问。

分析:

  •  生产者每次要消耗(P),一个空闲缓冲区,并生产(V)一个产品。
  • 消费者每次要消耗(P),一个产品,释放一个空闲缓冲区(V)
  • 缓冲区的存放和取走,是互斥的。

通过上述分析,需要三个信号量来表示。

semaphore mutex = 1;        //互斥信号量,实现对缓冲区的互斥访问
semaphore empty= n;        //同步信号量,表示空闲缓冲区的数量
semaphore full= 0;        //同步信号量,表示产品的数量,即非空缓冲区的数量

 具体实现

producer (){
    while(1){
        生产一个产品;
        p(empty);    //消耗一个空闲缓冲区
        p(mutex);    //pv连用,是实现互斥操作
        把产品放入缓冲区;
        v(mutex);    
        v(full);    //增加一个产品
    }
}

consumer (){
    while(1){
        p(full);    //消耗一个产品
        p(mutex);
        从缓冲区取出一个产品
        v(mutex);
        v(empty);    //增加一个空闲区
    }
}

实现互斥的p操作,一定要在实现同步的p操作之后

原因是,如果先执行互斥操作p(mutex),在执行同步操作p(empty),如果此时没有空闲的缓冲区,那么生产者就会被阻塞,切换回消费者进程。

所以最应该关注的点,应该是实现互斥和同步的pv操作的先后顺序。 

多生产者-多消费者问题

 

分析:

互斥信号量一般为1。

同步信号量需要看,对应资源的数量。

可以看到题目中,爸爸只能向盘子放苹果,苹果只能女儿吃。妈妈只能向盘子放橘子,橘子只能

儿子吃。

那么同步关系就是,父亲先放苹果,女儿才能取。

妈妈先放橘子,儿子才能拿。

只有盘子为空的时候,父亲或者妈妈才能放水果。

信号量设置如下:

semaphore mutex = 1;        //实现互斥访问盘子(缓冲区)
semaphore apple = 0;        //盘子中有几个苹果
semaphore orange = 0;       //盘子中有几个橘子
semaphore plate =1;        //盘子中海能放几个水果

dad(){

    while(1){
        准备一个苹果;
        p(plate);    //占用盘子,放水果
        p(mutex);    //pv操作连用,实现互斥
        苹果放入盘子;
        v(mutex);    
        v(apple);    //盘子中有一个苹果了
    }
}

daughter(){
    while(1){
        p(apple);    //拿苹果
        p(mutex);
        从盘子取出苹果;
        v(mutex);
        v(plate);    //放回盘子
    }
}
mom(){
    while(1){
        p(plate);    //占用盘子
        p(mutex);
        放入橘子;
        v(mutex);
        v(orange);    //放回盘子
    }
}
son(){
     while(1){
        p(orange);    //拿橘子
        p(mutex);
        从盘子取出橘子;
        v(mutex);
        v(plate);    //放回盘子
    }
}


可以发现的是,在本次的过程当中,apple,orange,plate,三个同步信号量,最多的是1,因此不会被阻塞,所以这样的过程,没有mutex也可以。

如果有两个盘子呢?那么就有可能会出现,两个进程同事访问缓冲区的问题了,有可能导致两个缓冲区的数据互相覆盖,所以就必须要设置一个mutex来保持互斥。

读者-写者问题

读者和写者是两组并发进程。

读和写一起执行有可能会导致,读到脏数据。

写和写一起执行,有可能导致数据错误。

 

 分析:

两类进程:读进程,写进程。

互斥关系:写进程-写进程,写进程-读进程。

需要设置一个互斥信号量rw,来保证写进程和任何进程都保持互斥。

需要一个count来计算,在写操作的过程当中,有多少个进程执行了读操作。那么问题就来了,count需要互斥吗?当然是需要的,首先需要count来判断这个进程是不是第一个读进程,是第一个读进程,需要上锁。再者如果两个读进程,先后执行P(rw),那么,就会导致第二个进程拿不到资源,阻塞。造成的原因就是对于count进程没有一气呵成,所以需要互斥。

代码如下:

semaphore    rw=1;        //用于实现对文件的互斥访问,表示的是当前有没有进程正在访问共享文件
int          count=0;     //记录有几个读进程,正在访问文件
semaphore    mutex=1;     //用来保证对于count变量的互斥访问

writer(){
    while(1){
        P(rw);    //写之前加锁            PPP操作
        写文件...
        V(rw);    //写完了                VVV操作
    }
}


reader(){
    while(1){
        P(mutex);        //各进程互斥访问count
        if(count == 0)
            P(rw);        //第一个读进程负责加锁           PPP操作
        count++;          //记录访问文件的读进程数量
        V(mutex);
        读文件
        P(mutex);         //各进程互斥访问count
        count--           
        if(count == 0)    
            V(rw);        //最后一个读进程负责解锁         VVV操作
        V(mutex);
    }
}

可以看见,上述代码的缺陷就是,读进程的优先级过高(先判断的P(mutex)就是将读进程上锁),当读进程过多的时候,那么写进程就迟迟拿不到资源,就会出现饿死的情况。

所以根据上述的缺陷,可以调整一下读写的优先级,即增加一个信号量w用于保证,写进程的优先级要高于读进程。

代码如下:

semaphore    rw=1;        //用于实现对文件的互斥访问,表示的是当前有没有进程正在访问共享文件
int          count=0;     //记录有几个读进程,正在访问文件
semaphore    mutex=1;     //用来保证对于count变量的互斥访问
semaphore    w=1;         //保证写进程的优先级

writer(){
    while(1){
        P(w)
        P(rw);    //写之前加锁            PPP操作
        写文件...
        V(rw);    //写完了                VVV操作
        V(w);
    }
}


reader(){
    while(1){
        P(w);
        P(mutex);        //各进程互斥访问count
          ....省略代码
        V(mutex);
        V(w);
    }
}

吸烟者问题

分析

第一个操作者有烟草-->拿纸和胶水

第二个操作者有纸-->拿烟草和胶水

第三个操作者有叫说-->拿烟草和纸

供应者能够放的材料组合有:

纸和胶水

烟草和胶水

烟草和纸

为了实现轮流抽烟的效果,还需要一个变量在0-3之间循环。

代码如下:

semaphore        offer1=0;    //桌上的1组合纸和胶水
semaphore        offer2=0;    //桌上的2组合烟草和胶水
semaphore        offer3=0;    //桌上的3组合烟草和纸
semaphore        finish=0;    //抽烟是否完成
int              i=0;         //实现循环抽烟的变量


provider(){
    while(1){
        if(i=0){
        将组合1放到桌子上
        V(offer1);
    }
    else if(i=1){
        将组合2放到桌子上
        V(offer2);
    }
    else if(i=2){
        将组合3放到桌子上
        V(offer3);
    }
    i=(i+1)%3;    //循环一轮后将i置为0
    p(finish);
    }
}

smoker1(){
    while(1){
    P(offer1);    //上锁
    拿掉桌子上的组合1
    V(finish);    //完成操作
}
}

smoker2(){
    while(1){
    P(offer2);    //上锁
    拿掉桌子上的组合2
    V(finish);    //完成操作
}
}

smoker3(){
    while(3){
    P(offer3);    //上锁
    拿掉桌子上的组合3
    V(finish);    //完成操作
}
}

哲学家进餐问题

 

 分析,因为需要拿到两根筷子才能吃饭,而如果几个哲学家一起拿的话,就会造成死锁(都在等待对方释放筷子给自己),怎么避免这种情况,有不同的策略。

  • 可以对拿筷子的哲学家进行限制,例如最多只有4名哲学家拿筷子,这样就保证了,至少有一名哲学家是拥有一双筷子的,这样就能够达到至少有一个哲学家能够用完筷子,再释放,从而避免死锁。
  • 仅当哲学家左右两根筷子都能够使用的时候,才允许他抓起筷子

其实,不需要这么麻烦,只需要将哲学家拿筷子这件事情,设置为互斥的方式,即每次只允许一名哲学家拿筷子,就算最后一名哲学在只拿到一根筷子,暂时阻塞,但是其他哲学家肯定有拿到两个筷子的,只需要等待就好了,并不会造成死锁。

semaphore        chopsticks[5]={1,1,1,1,1}        //表示5跟筷子
semaphore        mutex=1;        //互斥地拿筷子


Pi(){
    while(1){
    p(mutex);
    p(chopsticks[i])        //拿起左边的筷子
    p(chopsticks[(i+1)%5])  //拿起右边的筷子
    V(mutex)
    吃饭。。。
    V(chopsticks[i])
    V(chopsticks[(i+1)%5])  //放下左右筷子
    思考。。。
}
}

总结,这些问题,归根结底,需要考虑的是,哪里需要设置互斥,需要设置几个互斥信号量。而做这些的操作都是为了避免死锁,在速度方面似乎并没有考虑过多。 

 参考:王道计算机考研 操作系统_哔哩哔哩_bilibili

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ybbgrain

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值