408操作系统 PV专题

PV基础

cpp 复制代码
typedef struct {
	int value;
	struct process* l;//等待队列 
}semaphore;
 
void wait(semaphore s){// P() 
	s.value--;
	if(s.value<0) {
		add this process to s.l
		block(s.l)//block原语使进程从运行态->阻塞态 
	}
}
void signal(semaphore s){// V() 
	s.value++;
	if(s.value<=0) {
		remove a process p from s.l
		wakeup(p)//唤醒等待队列的一个进程 
	}
}

本质其实就是实现两个操作

1.互斥访问

cpp 复制代码
semaphore mutex=1;
p(){
	while(1){
		wait(mutex)		
		op
		signal(mutex) 
	}
} 

2.先A后B

cpp 复制代码
semaphore mutex=0;
A(){
	while(1){	
		op
		signal(mutex) //mutex++
	}
} 
B(){
	while(1){
		wait(mutex)	//mutex--,实现加完才能减	
		op
	}
} 

经典同步问题

生产者消费者问题

1.实现缓冲区互斥访问 mutex=1

2.先生产再消费 empty=n full=0

cpp 复制代码
semaphore mutex=1
semaphore empty=n,full=0
producer(){
	while(1){
		wait(empty)
		
		wait(mutex)
		放入缓冲区
		signal(mutex) 
		
		signal(full)
	}
} 
consumer(){
	while(1){
		wait(full)
		
		wait(mutex)
		从缓冲区取出 
		signal(mutex) 
		
		signal(empty)
	}
}

读者 - 写者问题

1.允许多个读者同时读取资源。

2.确保当有一个写者访问资源时,没有其他读者或写者可以同时访问。

cpp 复制代码
int count=0;//在读数
semaphore mutex=1;//实现count互斥访问
semaphore rw=1;//实现写时只能一个写 
writer(){
	while(1){
		wait(rw)
		写
		signal(rw) 
	}
} 
reader(){
	while(1){
		wait(mutex)
		if(count==0) p(rw);//没人读时,修改信号不可写 
		count++ 
		signal(mutex)
		
		读
		wait(mutex)
		count--;//读完释放 
		if(count==0) v(rw);//没人在读时,修改信号可写 
		signal(mutex) 
	}
}

但是上面代码有一个问题就是,只要有人在读就写不了,可以引入一个信号量实现写者公平排队

cpp 复制代码
int count = 0;           // 当前正在读的读者数
semaphore mutex = 1;     // 保护 count 的互斥量
semaphore rw = 1;        // 控制对共享数据的读写互斥(写者独占,读者并发)
semaphore w = 1;         // 用于给写者"优先权"的门

writer() {
    while (1) {
        wait(w);        // 申请"门"以表明有写者想写(阻止新读者进入)
        wait(rw);       // 等待没有读者或写者在占用(真正进入写区)
        // 写操作(独占访问共享数据)
        signal(rw);     // 写完释放对共享数据的占用
        signal(w);      // 放弃"门",允许其他读者/写者继续
    }
}

reader() {
    while (1) {
        wait(w);            // 等待门,若有写者已取得门,则新读者在此阻塞
        wait(mutex);        // 保护对 count 的访问
        if (count == 0)     // 第一个读者阻塞写者(占用 rw)
            wait(rw);
        count++;
        signal(mutex);
        signal(w);          // 立即释放门,允许写者/读者竞争(但若有写者已在门外等待,则门会被写者占用)
        
        // 读操作(可以与其他读者并发)
        
        wait(mutex);
        count--;
        if (count == 0)     // 最后一个读者释放对写的阻塞
            signal(rw);
        signal(mutex);
    }
}

哲学家就餐问题

解决死锁问题

1.允许进餐人数-1

cpp 复制代码
//五个人至多四个进餐
semaphore count=4;
semaphore chopsticks[4] = {1,1,1,1};

philospher(){
	while(1){
		wait(count)
		wait(chopsticks[i])
		wait(chopsticks[(i+1)%5])
		
		eat
		
		signal(chopsticks[i])
		signal(chopsticks[(i+1)%5])
		signal(count)
	}
} 

2.左右筷子都有时才拿(一次拿走想要的所有资源

cpp 复制代码
//五个人至多四个进餐
semaphore mutex=1;
semaphore chopsticks[5] = {1,1,1,1,1};

philospher(){
	while(1){
		wait(mutex)
		wait(chopsticks[i])
		wait(chopsticks[(i+1)%5])
		signal(mutex)
		
		eat......
		
		signal(chopsticks[i])
		signal(chopsticks[(i+1)%5])
	}
} 

还有一种写法是序号奇的先拿左边,偶的先拿右边,代码无非加个if判断就不写了

真题

09

这题不同的地方在于取奇数和偶数,因此需要信号量判断有没有奇数/偶数存在,把原来的full改成odd和even即可。

具体函数如何实现统计个数不用操心,调用题面给的函数就行。

cpp 复制代码
semaphore mutex=1;//实现缓冲区互斥访问
semaphore empty=n;
semaphore odd=0,even=0;
P1(){
	while(1){
		produce(a)
		
		wait(empty)
		wait(mutex)
		put(a)//放入缓冲区
		signal(mutex)
		
		if(a%2) signal(even)
		else signal(odd)
	}
} 
P2(){
	while(1){
		wait(odd)
		wait(mutex)
		getodd()//取出奇数 
		signal(mutex)
		signal(empty) 
		
		countodd()
	}
} 
P2(){
	while(1){
		wait(even)
		wait(mutex)
		geteven()//取出奇数 
		signal(mutex)
		signal(empty) 
		
		counteven()
	}
} 

11

cpp 复制代码
1.取号机互斥访问
2.有空才能取
3.营业员空闲叫号
semaphore mutex=1;//实现取号机互斥访问 
semaphore empty=10,full=0;
semaphore service=0;//实现营业员叫号后顾客才能获取服务 
cobegin
{
    process 顾客 i 
    {
    	wait(empty)
    	wait(mutex)
        从取号机获得一个号码;
        signal(mutex)
        signal(full)
        
        wait(service)
        等待叫号;
        获得服务;
    }

    process 营业员
    {
        while (TRUE) {
        	wait(full)
            叫号;
            signal(empty)//释放座位
			 
			signal(service) 
            为顾客服务;
        }
    }
}coend

13

最简单一题说是,仔细就行

cpp 复制代码
最多可以容纳 500 人同时参观,有一个出入口,
该出入口一次仅允许一个人通过。

semaphore empty=500;
semaphore mutex=1;
cobegin
参观者进程 i:
{
    wait(empty)
    wait(mutex)
    进门;
    signal(mutex)
    
    参观;
    
    wait(mutex)
    出门;
    wait(mutex)
    signal(empty)
}
coend

14

这题的点主要在于:连续取出 10 件产品后,其他消费者进程才可以取产品。

也就是要把一个顾客连续取十件产品维护成一个原子操作,单独开一个mutex用来实现互斥,在里面for取产品即可,有点像生产者消费者问题里面一次取出所有需要的资源。

cpp 复制代码
1000 件产品的环形缓冲区(初始为空)
当缓冲区未满时,生产者进程可以放入其生产的一件产品,否则等待;
当缓冲区未空时,消费者进程可以从缓冲区取走一件产品,否则等待。
连续取出 10 件产品后,其他消费者进程才可以取产品。
semaphore empty=1000;
semaphore mutex=1;//实现缓冲区互斥访问 
semaphore fmutex=1;//实现顾客互斥访问 
semaphore full=0;
producer(){
	while(1){
		生产
		wait(empty)
		wait(mutex)
		放入缓冲区
		signal(mutex)
		
		signal(full)
	}
}
consumer(){
	while(1){
		wait(fmutex)//实现消费者互斥 
		for(int i=1;i<=10;i++){
			wait(full)
			
			wait(mutex)
			取产品
			siganl(mutex) 
			
			signal(empty)
		}
		signal(fmutex)
	}
}

15

放心大胆用semaphore就行,看了下真题好像没用int的。

定义空位和满座位,模拟即可。

cpp 复制代码
a初始x最多m,b初始y最多n
从自己的信箱中取得对方的问题,
将一个邮件放入对方的信箱中。 
semaphore A_full = x;        // A 信箱中已有的邮件个数
semaphore A_empty = M - x;   // A 信箱还可以放多少个邮件
semaphore B_full = y;        // B 信箱中已有的邮件个数
semaphore B_empty = N - y;   // B 信箱中还能放多少个邮件
semaphore A_mutex = 1;       // 互斥访问 A 信箱
semaphore B_mutex = 1;       // 互斥访问 B 信箱

A() {
  while (1) {
    P(A_full);
    P(A_mutex);
    从A信箱中取出一个邮件;
    V(A_mutex);
    V(A_empty);
    
    回答问题并提出新问题;
    
    P(B_empty);
    P(B_mutex);
    将信件放入B邮箱;
    V(B_mutex);
    V(B_full);
  }
}

B() {
  while (1) {
    P(B_full);
    P(B_mutex);
    从B信箱中取出一个邮件;
    V(B_mutex);
    V(B_empty);
    
    回答问题并提出新问题;
    
    P(A_empty);
    P(A_mutex);
    将信件放入A邮箱;
    V(A_mutex);
    V(A_full);
  }
}

19

题目"尽可能多的哲学家用餐"指的是:在不产生死锁的前提下,使能同时进餐的哲学家数量最大化 。

这里的最大值 并不是 n ,而是 n − 1。

为什么不是 n?因为 所有 n 个哲学家同时拿起左筷子,会导致必然死锁 。

要避免死锁,系统必须在某个时刻 至少有 1 个哲学家不能拿起筷子 ,因此最大可同时进入"尝试就餐区"的哲学家只能是 n − 1。

cpp 复制代码
//防止死锁且经可能多的用餐 
//拿走所有资源再运行
semaphore chopsticks[n]={1};
semaphore plate=min(n-1,m);
philospher(int i){
	while(1){
		wait(plate)
		wait(chopsticks[i])
		wait(chopsticks[(i+1)%n])
		用餐 
		signal(chopsticks[i])
		signal(chopsticks[(i+1)%n])
		signal(plate)
	}
} 

21

1)信号量 S是能被多个进程共享的变量,多个进程都可通过 wait() 和 signal() 对 S 进行读、写操作。所以,wait() 和 signal() 操作中对 S 的访问必须是互斥的。

2)方法 1 错误。在 wait() 中,当 S≤0 时,关中断后,其他进程无法修改 S 的值,while 语句陷入死循环。方法 2 正确。方法 2 在循环体中有一个开中断操作,这样就可以使 其他进程修改 S 的值,从而避免 while 语句陷入死循环。

3)用户程序不能使用开/关中断指令实现临界区互斥。因为开中断和关中断指令都是特权 指令,不能在用户态下执行,只能在内核态下执行。

22

简单同步关系,要让a在b前执行,开一个信号量在a最后v,b最开始p就行。

cpp 复制代码
sempahore AC = 0;
sempahore CE = 0;

T1() {
    A;
    signal(AC);
    wait(CE);
    E;
    F;
}

T2() {
    B;
    wait(AC);
    C;
    signal(CE);
    D;
}

25

water只有丙会用

甲和是属于只要能挖坑就一直挖,乙是只要能放树苗填土就一直填

但丙的浇水动作不是无脑的,得等填完土才能浇水,所以water用于实现同步,乙做完丙就做。

cpp 复制代码
三个人一起植树,甲挖坑,乙放树苗入坑并填土,丙负责为新种树苗浇水。
步骤依次为:挖树坑,放树苗,填土和浇水。
现在有铁锹和水桶各一个,铁锹用于挖树坑,填土。
水桶用于浇水。当树坑数量小于 3 时,甲才可以挖树坑。
设初始坑 = 0,铁锹水桶均可用,定义尽可能少的信号量,
semaphore empty=0;//坑数目 
semaphore full=3;
semaphore tieqiao=1,water=0;
cobegin() {
	甲(){
		while(1){
			wait(full)
			wait(tieqiao)//互斥访问铁锹 
			挖坑 
			signal(tieqiao)
			signal(empty)
		}
	} 
	乙(){
		while(1){
			wait(empty)
			放树苗入坑 
			wait(tieqiao)//互斥访问铁锹 
			填土 
			signal(tieqiao)
			signal(full)
			
			signal(water) 
		}
	} 
	丙(){
		while(1){
			wait(water)
			浇水  
		}
	}
}
 

写代码比考试快乐多了,即使是伪代码。我讨厌408。

相关推荐
星夜离尘563 分钟前
MATLAB 工程实验:从相邻块均值手写重叠 Allan 偏差,数清有效项
算法·matlab·均值算法
酷炫码神28 分钟前
Day 10|Pinia 状态管理:把散落在各组件的数据,收进一个全局仓库
android·开发语言·javascript
Elastic 中国社区官方博客1 小时前
我们如何通过 70 多个拉取请求,将 OpenTelemetry Java agent 迁移到 invokedynamic
java·大数据·开发语言·elasticsearch·搜索引擎·全文检索·es
tyn1881 小时前
记录本地安装nacos服务的方法及异常
java·开发语言
步行cgn2 小时前
Spring MVC 视图机制的核心接口与类
java·开发语言·后端·spring·mvc
y1su2 小时前
Leetcode 字符串模板
java·数据结构·算法·leetcode·职场和发展·动态规划
传奇开心果编程3 小时前
【Rust入门练中学】第5课:引用与借用
开发语言·rust·引用·编程语言理论·通用编程语言·借用
residual_fan3 小时前
船用柴油机动态机动工况故障诊断:控制感知仿射网络(CAAN)的技术实现
人工智能·算法·数据挖掘·数据分析
qetfw3 小时前
Bash 变量:export、位置参数与默认值展开验证
linux·开发语言·chrome·bash
电子云与长程纠缠3 小时前
UE5魂系游戏开发教程05
开发语言·学习·游戏·ui·ue5