全知识整理目录
操作系统整理的目录,包括了整理的所有操作系统的知识。
目录
概述
如何使用信号量机制(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]) //放下左右筷子
思考。。。
}
}
总结,这些问题,归根结底,需要考虑的是,哪里需要设置互斥,需要设置几个互斥信号量。而做这些的操作都是为了避免死锁,在速度方面似乎并没有考虑过多。