目录
(6)进程同步:进程同步的概念和同步原则,临界资源和临界区的概念,信号量及其应用,经典进程同步问题
(6)进程同步:进程同步的概念和同步原则,临界资源和临界区的概念,信号量及其应用,经典进程同步问题
两种制约关系:
间接相互制约关系(进程互斥)
直接相互制约关系(进程同步)
-
进程同步的概念
进程同步,是指多个相互合作的进程,在一些关键点上,相互等待或交换信息。
-
进程同步的原则
a.空闲让进
当临界区空闲时,允许一个进程进入临界区。
b.忙则等待
已有进程进入临界区,其他试图进入临界区的进程等待。
c.有限等待
对于请求访问的进程,应让其在有限时间内进入临界区。
d.让权等待
当进程不能进入临界区时,应立即释放处理机防止忙等。
-
临界资源与临界区的概念
临界资源:一次仅允许一个进程使用的资源。
临界区:访问临界资源的那段代码。
repeat
entry section
critical section;
exit section
remainder section;
until false;
while(TRUE){
进入区
临界区
退出区
剩余区
}
-
信号量
a.整型信号量
一个用于表示资源数目的整型量S。
除初始化之外,仅能通过两个标准的原子操作wait(S)和signal(S)来访问。(P、V操作)
【注】P操作和V操作在执行路径上一一匹配。
wait(S){
while(S<=0)
S--;
}
signal(S){
S++;
}
b.记录下信号量
不存在“忙等”现象,增设一个进程链表指针list。
每次wait操作->资源数减少一个
1)S>0 S的值表示可用资源数
2)S=0 表示无可用资源
3)S<0 表示无可用资源且|S|表示在阻塞队列中等待的进程数目。
【注】若S的初值为1,表示只允许一个进程访问临界资源,此时信号量转化为互斥信号量,用于进程互斥。
c.AND型信号量
AND同步思想:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。(对若干临界资源的分配采取原子操作方式)
-
信号量的应用
1.利用信号量实现进程互斥
代码描述:
semaphore mutex = 1;
P1(){
while(1){
wait(mutex);
临界区;
signal(mutex);
剩余区;
}
}
P2(){
while(1){
wait(mutex);
临界区;
signal(mutex);
剩余区;
}
}
【注】互斥信号量mutex,初值为1,取值范围(-1,0,1)
1)当mutex=1时,表示两个进程皆未进入需要互斥的临界区;
2)当mutex=0时,表示有一个进程进入临界区运行,另一个必须等待,挂入阻塞队列;
2)当mutex=-1时,表示有一个进程进入临界区运行,另一个进程因等待而阻塞在信号量队列中,需要被当前已在临界区运行的进程退出时唤醒。
2.利用信号量实现前驱关系
cobegin 和 coend 与 parbegin 和parend 一样 都是并发进程,并行开始、并行结束的意思。
代码描述:
P1(){S1;sign(a);sign(b)}
P2(){wait(a);S2;sign(c);sign(d);}
P3(){wait(b);S3;sign(e);}
P4(){wait(c);S4;sign(f);}
P5(){wait(d);S5;sign(g);}
P6(){wait(e);wait(f);wait(g);S6}
main(){
semaphore a,b,c,d,e,f,g;
a = b = c = d = e = f = g = 0;
cobegain
P1();P2();P3();P4();P5();P6();
coend
}
【注】S1被初始化为0,只有在P1执行完S1后,signal(S)使S增为1时,P2才能成功执行S2。
-
经典进的同步问题
a.生产者消费者问题
(利用记录型信号量解决)
假定这些生产者和消费者相互等效,只要缓冲池未🈵️,生产者便可将消息送入缓冲池;只要缓冲池未🈳️,消费者便可从缓冲池中取走一个消息。
代码描述:
int in = 0, out = 0;
item buffer[n];//n个缓冲区
semaphore mutex = 1, empty = n, full = 0;//互斥信号量、空缓冲区、满缓冲区
void product(){
do{
producer an item nextp;//生产一件商品
...
wait(empty);
wait(mutex);
buffer[in] = nextp;
in = (in+1)%n;
signal(mutex);
signal(full);
}while(TRUE);
}
void consumer(){
do{
wait(full);
wait(mutex);
nextc = buffer[out];
out = (out+1)%n;
signal(mutex);
signal(empty);
consumer the item nextc;//消费一件产品
...
}while(TRUE);
}
void main(){
cobegin
producer();
consumer();
coend
}
【注】两个信号量的PV操作不可颠倒位置,否则会发生死锁问题。
用于实现互斥的wait(mutex)和signal(mutex)必须成对出现!!
b.哲学家进餐问题
(利用记录型信号量实现)
桌上的筷子🥢是临界资源,在一段时间内只允许一位哲学家👨使用。为实现对筷子🥢的互斥使用,可以用一个信号量📶表示一只筷子🥢,由者五个信号量构成信号量数组。
代码描述:
semaphore chopstick[5] = {1,1,1,1,1};
do{ //第i位哲学家的活动
wait(chopstick[i]); //当哲学家饥饿时,总先拿他左边的筷子
wait(chopstick[(i+1)%5]);//再拿他右边的筷子
...
//eat进餐
...
signal(chopstick[i]);
signal(chopstick[(i+1)%5]);
...
//think思考
...
}while(TRUE);
【注】当五位哲学家同时饥饿而各自拿起左边的筷子🥢,会使五个信号量chopstick为0,他们再想拿右边的筷子时,都因无筷子🥢可拿而无限等待⌛️。(死锁问题)
解决方法:
1)至多只允许4⃣️位哲学家同时去拿左边的筷子🥢,并在用毕时能释放他用过的两只筷子;
2)仅当哲学家左右↔️两只筷子均可使用时,才允许他拿起筷子进餐;
3)规定奇数号哲学家先拿起他的左筷子,再去拿右筷子,偶数号哲学家相反。
c.读者、写者问题
(利用记录型信号量实现)
代码描述:
semaphore rmutex = 1, wmutex =1;
int readcount = 0; //整型变量,表示正在读的进程数目(多个读者共享变量)
void reader(){
do{
wait(rmutex);
if(readcount == 0) wait(wmutex);//读者优先(可以保证多个读者同时读)
readcount ++;
signal(rmutex);
...
perform read operation;
...
wait(rmutex);
readcount--;
if(readcount == 0) signal(wmutex);
signal(rmutex);
}while(TRUE);
}
void write(){
do{
wait(wmutex);
perform write operation;
signal(wmutex);
}while(TRUE);
}
void main(){
cobegin
reader();
write();
coend
}