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。

相关推荐
天一生水water1 小时前
储层认知→技术落地→产量优化
人工智能·算法·机器学习
浩瀚地学1 小时前
【Java】ArrayList
java·开发语言·经验分享·笔记
明洞日记1 小时前
【VTK手册019】 深入理解 vtkProperty:从几何表达到 PBR 物理渲染
c++·图像处理·算法·vtk·图形渲染
阿杰同学1 小时前
Java 设计模式 面试题及答案整理,最新面试题
java·开发语言·设计模式
这样の我1 小时前
java 模拟chrome指纹 处理tls extension顺序
java·开发语言·chrome
yong99901 小时前
基于MATLAB的雷达压制干扰仿真
开发语言·matlab
Genevieve_xiao1 小时前
【数据结构与算法】【xjtuse】面向考纲学习(下)
java·数据结构·学习·算法
修炼地1 小时前
代码随想录算法训练营第二十七天 | 56. 合并区间、738.单调递增的数字、968.监控二叉树
c++·算法
仰泳的熊猫1 小时前
1031 Hello World for U
数据结构·c++·算法·pat考试