🌈 个人主页:十二月的猫-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及课上讲解、山东大学操作系统往年题、部分考研题。不要私下外传