一、生产者与消费者问题
问题描述:一组生产者进程和一组消费者进程共享一块初始为空,大小确定的缓冲区,只有当缓冲区为满时,生产者进程才可以把信息放入缓冲区,否则就要等待;只有缓存区不为空时,消费者进程才能从中取出消息,否则就要等待。缓冲区一次只能一个进程访问(临界资源)。
问题分析:生产者与消费者进程对缓冲区的访问是互斥关系,而生产者与消费者本身又存在同步关系,即必须生成之后才能消费。因而对于缓冲区的访问设置一个互斥量,再设置两个信号量一个记录空闲缓冲区单元,一个记录满缓冲区单元来实现生产者与消费者的同步。
问题解决:伪代码实现
semaphore mutex=1;
semaphore full=0; //满缓冲区单元
semaphore empty=N; //空闲缓冲区单元
prodecer()
{
while(1)
{
P(empty);
P(mutex);
add_source++;
V(mutex);
V(full);
}
}
consumer()
{
while(1)
{
P(full);
P(mutex);
add_source--;
V(mutex);
V(empty);
}
}
二、读者与写者问题
问题描述:有读者与写者两个并发进程共享一个数据,两个或以上的读进程可以访问数据,但是一个写者进程访问数据与其他进程都互斥。
问题分析:读者与写者是互斥关系,写者与写者是互斥关系,读者与读者是同步关系。因而需要一个互斥量实现读与写和写与写互斥,一个读者的访问计数和实现对计数的互斥。
问题解决:三种伪代码实现
1、读者优先
读者优先,只要有读者源源不断,写者就得不到资源。容易造成写者饥饿。
1 //读者优先
2
3 int count=0;
4 semaphore mutex=1; //读者计数锁
5 semaphore rw=1; //资源访问锁
6
7 writer()
8 {
9 while(1)
10 {
11 P(rw);
12 writing sth;
13 V(rw);
14 }
15 }
16
17 reader()
18 {
19 while(1)
20 {
21 P(mutex);
22 if(count==0)
23 P(rw);
24 count++;
25 V(mutex);
26 reading sth;
27 P(mutex);
28 count--;
29 if(count==0)
30 V(rw);
31 V(mutex);
32 }
33 }
2、读写公平
读者与写者公平抢占资源,但是只要之前已经排队的读者,就算写者获取的资源,也要等待所有等待的读者进程结束。
1 //读写公平
2 int count=0;
3 semaphore mutex=1; //读者计数锁
4 semaphore rw=1; //资源访问锁
5 semaphore w=1; //读写公平抢占锁
6 writer()
7 {
8 while(1)
9 {
10 P(w);
11 P(rw);
12 writing sth;
13 V(rw);
14 V(w);
15 }
16 }
17
18 reader()
19 {
20 while(1)
21 {
22 P(w);
23 P(mutex);
24 if(count==0)
25 P(rw);
26 count++;
27 V(mutex);
28 V(w);
29 reading sth;
30 P(mutex);
31 count--;
32 if(count==0)
33 V(rw);
34 V(mutex);
35 }
36 }
3、写者优先
写者优先,只要写者源源不断,读者就得不到资源,但是在这之前已经排队的的读者进程依然可以优先获得资源,在这之后则等待所有写者进程的结束。这种也易造成读者饥饿。
1 //写者优先
2 int write_count=0; //写计数
3 int count=0; //读计数
4 semaphore w_mutex=1; //读计数时锁
5 semaphore r_mutex=1; //写计数时锁
6 semaphore rw=1; //写优先锁
7 semaphore source=1; //资源访问锁
8
9 writer()
10 {
11 while(1)
12 {
13 P(w_mutux);
14 if(write_count==0)
15 P(rw); //获得则只要有写进程进来就不释放
16 write_count++;
17 V(w_mutux)
18
19 P(resouce); //写时互斥必须加资源独占的锁
20 writing sth;
21 V(resouce);
22
23 P(w_mutux);
24 write_count--;
25 if(write_count==0)
26 V(rw);
27 V(w_mutux);
28 }
29 }
30
31 reader()
32 {
33 while(1)
34 {
35 P(rw); //使用了立即释放
36 P(r_mutex);
37 if(count==0)
38 P(resouce);
39 count++;
40 V(r_mutex);
41 V(rw);
42
43 reading sth;
44
45 P(r_mutex);
46 count--;
47 if(count==0)
48 V(resouce);
49 V(r_mutex);
50 }
51 }
三、哲学家就餐问题
问题描述:一张圆桌上坐着五名哲学家,每两名哲学家之间的桌子摆一根筷子,哲学家只有同时拿起左右两根筷子时才可以用餐,用餐完了筷子放回原处。
问题分析:这里五名哲学家就是五个进程,五根筷子是需要获取的资源。可以定义互斥数组用于表示五根筷子的互斥访问,为了防止哲学家个取一根筷子出现死锁,需要添加一定的限制条件。一种方法是限制仅当哲学家左右筷子均可以用时,才拿起筷子,这里需要一个互斥量来限制获取筷子不会出现竞争。
问题解决:一次仅能一个哲学家拿起筷子,效率比较低。
1 semaphore chopstick[5]={1,1,1,1,1};
2 semaphore mutex=1;
3 pi()
4 {
5 while(1)
6 {
7 P(mutex);
8 P(chopstick[i]);
9 P(chopstick[(i+1)%5]);
10 V(mutex);
11
12 eating;
13
14 V(chopstick[i]);
15 V(chopstick[(i+1)%5]);
16 }
17 }
理发师问题
@(操作系统)[进程同步]
description
假设有一个理发店只有一个理发师,一张理发时坐的椅子,若干张普通椅子顾客供等候时坐。没有顾客时,理发师就坐在理发的椅子上睡觉。顾客一到,他不是叫醒理发师,就是离开。如果理发师没有睡觉,而在为别人理发,他就会坐下来等候。如果所有的椅子都坐满了人,最后来的顾客就会离开。
在出现竞争的情况下问题就来了,这和其它的排队问题是一样的。实际上,与哲学家就餐问题是一样的。如果没有适当的解决方案,就会导致进程之间的“饿肚子”和“死锁”。
如理发师在等一位顾客,顾客在等理发师,进而造成死锁。另外,有的顾客可能也不愿按顺序等候,会让一些在等待的顾客永远都不能理发。
解决方案
最常见的解决方案就是使用三个信号量(Semaphore):一个给顾客信号量,一个理发师信号量(看他自己是不是闲着),第三个是互斥信号量(Mutual exclusion,缩写成mutex)。一位顾客来了,他想拿到互斥信号量,他就等着直到拿到为止。顾客拿到互斥信号量后,会去查看是否有空着的椅子(可能是等候的椅子,也可能是理发时坐的那张椅子)。
如果没有一张是空着的,他就走了。如果他找到了一张椅子,就会让空椅子的数量减少一张,这位顾客接下来就使用自己的信号量叫醒理发师。这样,互斥信号标就释放出来供其他顾客或理发师使用。如果理发师在忙,这位顾客就会等。理发师就会进入了一个永久的等候循环,等着被在等候的顾客唤醒。一旦他醒过来,他会给所有在等候的顾客发信号,让他们依次理发。
PV操作
顾客信号量 = 0
理发师信号量 = 0
互斥信号量mutex = 1 // 椅子是理发师和顾客精进程都可以访问的临界区
int 空椅子数量 = N //所有的椅子数量
理发师(线程/进程)
While(true){ //持续不断地循环
P(顾客) //试图为一位顾客服务,如果没有他就睡觉(进程阻塞)
P(互斥信号量) //如果有顾客,这时他被叫醒(理发师进程被唤醒),要修改空椅子的数量
空椅子数量++ //一张椅子空了出来
V(理发师) //现在有一个醒着的理发师,理发师准备理发,多个顾客可以竞争理发师互斥量,但是只有一个顾客进程可以被唤醒并得到服务
V(互斥信号量) //释放椅子互斥量,使得进店的顾客可以访问椅子的数量以决定是否进店等待
/* 理发师在理发 */
}
顾客(线程/进程)
while(true)
{ //持续不断地循环
P(互斥信号量) //想坐到一张椅子上
if (空椅子数量 > 0)
{ //如果还有空着的椅子的话
空椅子数量-- //顾客坐到一张椅子上了
V(顾客) //通知理发师,有一位顾客来了
V(互斥信号量) //顾客已经坐在椅子上等待了,访问椅子结束,释放互斥量
P(理发师) //该这位顾客理发了,如果理发师还在忙,那么他就等着(顾客进程阻塞)
/* 竞争到了理发师则该顾客开始理发 */
}
else
{ //没有空着的椅子
V(互斥信号标) //不要忘记释放被锁定的椅子
/* 顾客没有理发就走了 */
}
}
补充内容
死锁:如果一个进程集合中的每个进程都在等待只能由该进程集合中的其他进程才能引发的事件,那么该进程集合就是死锁的。
死锁的条件(四个同时满足):
(1)互斥:每个资源要么已经分配给一个进程,要么就是可用的;
(2)占有和等待:已经得到的某个资源的进程请求新的资源;
(3)不可抢占:已经分配的资源不能强制被抢占,只能进程自己显示的释放;
(4)环路等待:存在一种进程资源的循环等待链。
死锁的处理策略:
(1)死锁预防:破坏死锁的四个条件之一
破环互斥条件:允许资源共享
破环占有和等待条件:采用预先静态分配
不可抢占:请求新资源得不到时,释放已经保持占有的资源,待以后重新申请
环路等待:采用顺序资源分配法
(2)死锁避免:死锁避免事先预防策略,但是是采用资源动态分配的过程中,防止系统进入不安全状态,以避免死锁。
银行家算法:可利用资源矢量Available,请求矢量Request
最大需求矩阵Max,分配矩阵Allocation,需求矩阵Need
通过Need=Max-Allocation获得每个进程需要的各类资源数Need矩阵
一般每个进程请求矢量应该小于等于Need的值
试探分配:Available=Avaliable-Request
Allocate相对应的项=Allocate相对应的项+Request
Need相对应的项=Need相对应的项-Request
安全性算法:检查资源分配后,系统是否属于安全状态,如果安全才正式分配资源,否则作废。一般通过安全性算法推算一个安全序列(核心)。
(3)死锁检测与解除:
检测死锁:利用死锁原理化简资源分配图检测死锁的存在
死锁解除:资源剥夺、撤销进程、进程回退