操作系统精选题(一)(PV经典问题之生产者与消费者)

🌈 个人主页:十二月的猫-CSDN博客
🔥 系列专栏: 🏀操作系统

💪🏻 十二月的寒冬阻挡不了春天的脚步,十二点的黑夜遮蔽不住黎明的曙光 

目录

前言

进程互斥与同步

 题目一

题目二

题目三

题目四

题目五

题目六

题目七

题目八

题目九

题目十

题目十一

总结


前言

本系列的重点是大学本科课程《操作系统概念》。总结操作系统学习中的各类知识点,并且对各类操作系统试中可能遇到的题型和对应的解法做总结归纳。在自我复习的同时也将这份心得带给大家,希望能对大家的操作系统学习提供帮助~~

进程互斥与同步

 题目一

1、wait与signal为什么要设计成原语?

解:

原语是指不可分的操作,是具有原子性的操作。安全性:wait和signal操作是用于信号量的控制,在信号量的变化过程中不允许其他进程抢占这个进程;原子性:wait和signal操作要求是原子的,也就是说这两个操作必须是不可分的,要么全完成,要么全不完成,不存在中间状态;一致性:wait和signal操作在所有系统的信号量处理中都要使用到,原语保证其是通用的,保持在所有系统中一致

题目二

经典PV问题——有界缓存

1、存在临界资源——>定义互斥锁

2、有界缓存——>定义信号量full、empty

3、生产者代码

4、消费者代码

生产者、消费者代码编写流程:

1、找进程:确定谁是消费者,谁是生产者 

2、找主营业务:一般都是一句话带过

3、找同步约束

  •  互斥
  • 资源(计数信号量、空闲空间信号量等)
  • 限额 

2、一个输入进程向一个缓冲区中输入数据,另一个输出进程从缓冲区中取出数据输出。缓冲区中每次只能存放一个数。

解:

semaphore empty=1;// 有界缓存区大小为1
semaphore full=0;// 开始有界缓冲区不为满
semaphore mutex=1;

producer{
    do{
        /*produce item*/
        wait(empty)
        wait(mutex)
        /*put item into the buffer*/
        signal(mutex)
        signal(full)
    }while(true)
}
consumer{
    do{
        wait(full)
        wait(mutex)
        /*get item from the buffer*/
        signal(mutex)
        signal(empty)
        /*consume the item*/
    }while(true)
}

题目三

有界缓存的变型

1、存在临界资源——>定义互斥锁

2、有界缓存——>定义信号量full、empty

3、输出进程只能拿加工后的数据——>进程存在执行顺序——>定义信号量去控制执行顺序

3、生产者代码

4、消费者代码

生产者:计算进程(送数)

消费者:加工进程、输出进程

输出进程和加工进程之间存在确定执行顺序 

3、三个进程共享一个缓冲区。一个计算进程送数;一个加工进程取出加工,然后将加工结果再送回缓冲区;一个输出进程将加工后的数据取出打印。缓冲区中每次只能存放一个数。

解:

semaphore empty=n;// 有界缓存区大小为n
semaphore full=0;// 开始有界缓冲区不为满
semaphore mutex=1;
semaphore processed=0;//已被加工的数据数量

producer{
    do{
        /*produce item*/
        wait(empty)
        wait(mutex)
        /*put item into the buffer*/
        signal(mutex)
        signal(full)
    }while(true)
}
process{
    do{
        wait(full)
        wait(mutex)
        /*get item from the buffer*/
        signal(mutex)
        signal(empty)
        /*process the item*/
        wait(empty)
        wait(mutex)
        /*put the item into buffer*/
        signal(mutex)
        signal(full)
        signal(processed)
    }while(true)
}
consumer{
    do{
        wait(full)
        wait(processed)
        wait(mutex)
        /*get item from the buffer*/
        signal(mutex)
        signal(empty)
        /*consume the item*/
    }while(true)
}

题目四

4、三个进程共享一个缓冲区。一个负责向缓冲区送数;一个取偶数输出,另一个取奇数输出。缓冲区中每次只能存放一个数。

解:

semaphore empty=n;// 有界缓存区大小为n
semaphore full=0;// 开始有界缓冲区不为满
semaphore mutex=1;
semaphore odd=0;//偶数信号量
semaphore even=0;//奇数信号量

producer{
    do{
        item=produce_item()
        wait(empty)
        wait(mutex)
        buffer.put(item)
        if item is even
            signal(even)
        else
            signal(odd)
        signal(mutex)
        signal(full)
    }while(true)
}
consumer1{
    do{
        wait(full)
        wait(odd)
        wait(mutex)
        item = buffer.get()
        signal(mutex)
        signal(empty)
        process(item) 
    }while(true)
}
consumer2{
    do{
        wait(full)
        wait(even)
        wait(mutex)
        item = buffer.get()
        signal(mutex)
        signal(empty)
        process(item) 
    }while(true)
}

题目五

5、四个进程共享一个缓冲区,一个送偶数,一个送奇数,一个取偶数,一个取奇数。缓冲区中每次只能存放一个数。

解:

semaphore empty=n;// 有界缓存区大小为n
semaphore full=0;// 开始有界缓冲区不为满
semaphore mutex=1;
semaphore odd=0;//偶数信号量
semaphore even=0;//奇数信号量

producer1{
    do{
        item=produce_item()
        wait(empty)
        wait(mutex)
        buffer.put(item)
        signal(even)
        signal(mutex)
        signal(full)
    }while(true)
}
producer2{
    do{
        item=produce_item()
        wait(empty)
        wait(mutex)
        buffer.put(item)
        signal(odd)
        signal(mutex)
        signal(full)
    }while(true)
}
consumer1{
    do{
        wait(full)
        wait(odd)
        wait(mutex)
        item = buffer.get()
        signal(mutex)
        signal(empty)
        process(item) 
    }while(true)
}
consumer2{
    do{
        wait(full)
        wait(even)
        wait(mutex)
        item = buffer.get()
        signal(mutex)
        signal(empty)
        process(item) 
    }while(true)
}

题目六

生产者-消费者问题变型——不存在生产者,利用初始化一次性完成生产

1、一次性完成生产也就不是有界缓存问题——>不需要full和empty

2、拿黑子,拿白子访问一个buffer——>互斥变量mutex

3、拿黑子和白子需要生产后才可以进行,存在先后执行顺序——>信号量white和black(也可以用普通变量来表示white和black的值,不用信号量)

4、先拿黑子,同时进程交替拿棋子,存在执行顺序——>信号量whiteTurn和blackTurn

6、围棋问题:数量相等的黑子与白子混在一起,利用两个进程分开。一个进程拣白子,另一个进程拣黑子。要求:

(1)一个进程拣了一个子,必须让另一个进程拣子;即两个进程应交替拣子;

(2)假定先拣黑子。

解:

semaphore mutex=1;
semaphore whiteTurn=0;
semaphore blackTurn=1;//先拿黑色棋子
semaphore black=n1;
semaphore white=n2;

# 拣黑子的进程
black_picker {
    do {
        wait(blackTurn)          # 等待轮到拣黑子
        wait(black)
        wait(mutex)              # 获取互斥锁,确保独占访问缓冲区
        if buffer has black piece:
            item = buffer.get_black()  # 获取一个黑子
            process(item)        # 处理黑子
        signal(mutex)            # 释放互斥锁
        signal(whiteTurn)        # 释放白子的信号量,让白子进程拣子
    } while (true)
}
# 拣白子的进程
white_picker {
    do {
        wait(whiteTurn)          # 等待轮到拣白子
        wait(white)
        wait(mutex)              # 获取互斥锁,确保独占访问缓冲区
        if buffer has white piece:
            item = buffer.get_white()  # 获取一个白子
            process(item)        # 处理白子
        signal(mutex)            # 释放互斥锁
        signal(blackTurn)        # 释放黑子的信号量,让黑子进程拣子
    } while (true)
}

题目七

进程前驱图作用:

1、体现进程间的执行顺序

2、体现进程间的依赖关系

3、体现进程是否存在死锁等问题

7、画出下面4条语句的前趋图(符号“:=”是赋值的意思)
S1:a:=x+y
S2:b:=z+1
S3:c:=a-b
S4:w:=c+1

解:

题目八

N<A产品数量 – B产品数量<M——>存在同步约束中的限额

限额:(共享量对资源的使用产生限制)(这里就是利用count差值来限制)(差值限额)

1、限额意味着限额要控制进程的活动——>设定两个信号量控制进程活动

2、限额本身需要两个变量来控制——>两个变量控制进程信号量资源的释放

3、限额的检查要在实际操作之前

8、有一个仓库,可以存放X与Y两种产品,仓库的存储空间足够大,但要求:

(1)每次只能存入一种产品(X或Y);

(2)N<A产品数量 – B产品数量<M;

其中,N和M是正整数。试用“存放A”和“存放B”和wait、signal描述产品A与产品B的入库过程。

解:

semaphore mutex=0;
semaphore sem_A=0;//控制A进程的信号量
semaphore sem_B=0;//控制B进程的信号量
int countA=0;
int countB=0;//共享的限额量,全局变量

storeA{
    wait(mutex);
    if countA-countB<M
        signal(sem_A)
    signal(mutex);

    wait(sem_A)
    wait(mutex)
    store(A);
    countA++;
    if countA-countB>N
        signal(sem_B)
    signal(mutex);
}
storeB{
    wait(mutex)
    if countA-countB>N
        signal(sem_B);
    signal(mutex)

    wait(sem_B)
    wait(mutex)
    store(B);
    countB++;
    if countA-countB<M
        signal(sem_A)
    signal(mutex)
}

注意!!

这里为了提高进程运行的效率也可以设定两个互斥信号量:1、mutex_buffer;2、mutex_count。实现对不同临界资源的限制 

1、确定生成者消费者

2、写主营业务

3、找限额控制进程信号量写上(常有两个对应的存在)

4、资源视角找同步约束(进程控制信号量)

5、找互斥

题目九

多生产者-多消费者问题+广播机制

1、多生产者-多消费者意味着实际运行需要开许多进程同步来跑

2、m个缓冲区每个放一个信息 等价于 一个缓冲区放m个信息

2、广播机制则要将一个缓冲区看为多个缓冲区,一个读进程单独享有一个缓冲区

9、进程A1、A2,……,An1通过m个缓冲区向进程B1,B2,……,Bn2不断地发送消息。发送和接收工作遵循如下规则:

(1)每个进程发送一个消息,写入一个缓冲区,缓冲区大小与消息长度一样;

(2)对每一个消息,B1,B2,……,Bn2都需各接收一次,读入各自的数据区中;

(3)m个缓冲区都满时,发送进程等待,没有可读取的消息时,接收进程等待。

试用wait与signal操作组织正确的发送和接收操作。

解:

int n2;//存在n2个读进程,这个值不是临界资源
semaphore empty[n2] = m;
semaphore full[n2] = 0;

//创建n1个写进程,都用下面的代码
producer{
    while(1)
    {
        for(int i=0;i<n2;i++)
        {
            P(empty[i]);
        }
        P(mutex);
        消息放入缓冲区;
        V(mutex);
        for(i = 0;i<n2;i++)
        {
            V(full[i]);
        }
    }
}
//创建n2个读进程,给不同的arg值和producer中的full和empty对应
consumer(void* arg){
    id=(int)arg
    while(1)
    {
        P(full[id]);
        P(mutex);
        读取缓冲区;
        V(mutex);
        V(empty[i]);
    }
}

题目十

本题和前面的限额问题没有区别,就是消费者消费、生产者生产需要按照队列去组织以实现FIFO

这里的限额是:差值限额

10、有一个仓库存放两种零件A和B,最大库容为各为m个。有一个车间不断地取A和B进行装配,每次各取一个。为避免零件锈蚀,遵循先入库者先出库的原则。有两组供应商分别不断地供应A和B(每次一个)。为保证齐套和合理库存,当某种零件的数量比另一种的数量超过n(n<m)个时,暂停对数量大的零件的进货,集中补充数量少的零件。试用wait和signal正确实现之。

解:

int m = 10; // 缓冲区大小
int n = 3;  // 数量差异限制
int n2;     // 读进程数量

semaphore mutex = 1; // 互斥信号量
semaphore sem_A = 0; // 控制A进程的信号量
semaphore sem_B = 0; // 控制B进程的信号量
int countA = 0;      // A零件计数器
int countB = 0;      // B零件计数器

void producerA() {
    while(1) {
        wait(mutex);
        if (countA - countB < m) {
            signal(sem_A);
        }
        signal(mutex);

        wait(sem_A);
        wait(mutex);
        // 消息放入缓冲区(存储A零件)
        countA++;
        printf("Stored A part, countA: %d\n", countA);
        if (countA - countB > n) {
            signal(sem_B);
        }
        signal(mutex);
    }
}

void producerB() {
    while(1) {
        wait(mutex);
        if (countB - countA < m) {
            signal(sem_B);
        }
        signal(mutex);

        wait(sem_B);
        wait(mutex);
        // 消息放入缓冲区(存储B零件)
        countB++;
        printf("Stored B part, countB: %d\n", countB);
        if (countB - countA > n) {
            signal(sem_A);
        }
        signal(mutex);
    }
}

void consumer(void* arg) {
    int id = (int)arg;
    while(1) {
        wait(sem_A); // 等待A零件
        wait(sem_B); // 等待B零件
        wait(mutex);
        // 从缓冲区读取零件A和B进行装配
        countA--;
        countB--;
        printf("Workshop assembled A and B, countA: %d, countB: %d\n", countA, countB);
        signal(mutex);
        signal(sem_A); // 通知可以放入新的A零件
        signal(sem_B); // 通知可以放入新的B零件
    }
}

题目十一

1、多生产者单消费者问题+有界缓冲+限额

2、在其他网站上看到很多人会选择增加一个guard进程来控制,但是实际上能够从限额的角度去看待这个PV问题,不需要增加一个监视进程

3、单消费者意味着不可能存在广播,没有广播需求即只有一个消费者在乖乖拿东西,那也就是说仅仅需要一个有界缓冲区即可(一个消费者只能拿一个缓冲区、一个缓冲区只能接受一个消费者。两者是一一对应关系) 

11、某高校计算机系开设网络课并安排上机实习。假定机房共有2m台机器,有2n个学生选该课,规定:

(1)每两个学生组成一组,各占一台机器,协同完成上机实习;

(2)只有一组两个学生到齐,并且此时机房有空闲机器时,该组学生才能进入机房;

(3)上机实习由一名教师检查,检查完毕,一组学生同时离开机房。

试用wait和signal正确实现之。

  • 学生是生产者
  • 教师是消费者
  • 电脑是有界缓冲区,也就是共享资源
  • 两个学生绑定为一个,也就是两台电脑绑定为一个。即有界缓存是m个资源
  • m个有界缓冲区每个一个空间可以认为是一个有界缓冲区有m个空间

解:

int m ; // 机房机器数量
int n ;  // 学生组数量(每组2个学生)

semaphore machine_num=m;    // 表示有界缓冲区的数量
semaphore mutex=1;            // 保护共享资源的互斥信号量
semaphore pair_sem=0;         // 用于限额释放的信号量(配对限额)
int waiting_students = 0;     // 记录已经到达的学生数量
semaphore teacher_sem=0;      // 表示教师可以进行检查
semaphore studentGroup_sem=0; // 表示学生可以离开

student{
    while(1){
        //学生过来
        wait(mutex)
        waiting_students++
        if(waiting_students%2==0)
            signal(pair_sem)
        signal(mutex)

        wait(pair_sem)
        wait(machine_num)
        wait(machine_num)
        wait(mutex)
        //操作电脑
        signal(mutex)
        signal(teacher_sem)
        wait(studentGroup_sem)
        //离开实验室
        signal(machine_num)
        signal(machine_num)
        
    }
}
teacher{
    wait(teacher_sem)
    wait(mutex)
    //检查学生作业
    signal(mutex)
    signal(studentGroup_sem)
}

总结

本文到这里就结束啦~~下节课我们再来进入剩下的三种调度算法
如果觉得对你有帮助,辛苦友友点个赞哦~

知识来源:操作系统概念(黑宝书)、山东大学高晓程老师PPT及课上讲解、山东大学操作系统往年题、部分考研题。不要私下外传

  • 32
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十二月的猫

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

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

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

打赏作者

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

抵扣说明:

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

余额充值