信号量
信号量机制是一种功能较强的机制,可用于解决互斥和同步的问题,它只能被两个标准的原语wait(S)和signal(S)访问,也可以记为“P操作”和“V操作”。
原语:指完成某种功能且不被分割,不被中断执行的操作序列,通常可由硬件来实现。例如TestAndSet和Swap指令就是通过硬件实现的原子操作。原语功能的不被中断执行特性在单处理机上可由屏蔽中断方法来实现。
1、整型信息量
整型信号量被定义为一个表示资源数量的整型量S,wait和signal操作可描述为
wait(S){
while(S<=0);
S = S - 1;
}
signal(S){
S = S + 1;
}
wait操作中,只要信号量S<=0,就会不断地测试。因此,该机制并未遵循“让权等待”,而是使进程处于忙等状态。
2、记录型信号量
记录型信号是不存在“忙等”现象的进程同步机制。 除需要一个用于代表资源数目的整型变量外,再层架一个进程链表L,用于连接所有等待该资源的进程。记录型信号量可描述为
typedef struct {
int value;
struct process *L;
}semaphore;
相应的wait(S)和signal(S)的操作如下:
void wait(semaphore S) {
S.value--;
if(S.value < 0) {
将这个进程P插入等待链表队列S.L;
block(P);//阻塞进程P
}
}
void signal(semaphore S) {
S.value++;
if(S.value <= 0) {
将等待链表队列S.L中的一个阻塞进程P移除;
wakeup(P);//唤醒进程P
}
}
利用记录型信号量实现同步(先执行x,再执行y)
semaphore S.value = 0; //初始化信号量
P1() {
x; //语句x
V(S); //告诉进程P2,语句x已经完成
}
P2() {
P(S); //检查语句x是否运行完成
y; //检查无误,运行y语句
}
利用记录型信号量实现互斥
semaphore S.value = 1;
P1() {
P(S); //准备开始访问临界资源,加锁
进程P1的临界区
V(S); //访问结束,解锁
}
P2() {
P(S); //准备开始访问临界资源,加锁
进程P2的临界区
V(S); //访问结束,解锁
}
利用记录型信号量实现前驱关系
实现算法如下:
semaphore a.value = 0;
semaphore b.value = 0;
semaphore c.value = 0;
semaphore d.value = 0;
semaphore e.value = 0;
semaphore f.value = 0;
semaphore g.value = 0;
S1(){
...;
V(a);V(b);
}
S2(){
P(a)
...;
V(c);V(d);
}
S3(){
P(b);
...;
V(g);
}
S4(){
P(c);
...;
V(e);
}
S5(){
P(d);
...;
V(f);
}
S6(){
P(e);
P(f);
P(g);
...;
}
3、AND型信号量
由信号量实现前驱关系可得,有时需要满足多个进程之间的同步关系,此时引入AND型信号量就比较方便了。
AND型信号量可描述为
Swait(S1,S2,……Sn) {
while(true) {
if(S!.value >=1 && …… && Sn >= 1) {
for(int i = 1; i <= n; i++) Si.value--;
break;
}
else {
找到第一个Si.value < 1,将P进程放进Si的等待链表队列
}
}
}
Ssignal(S1,S2,……Sn){
while(true) {
for(int i = 1; i <= n; i++) {
Si++;
将Si的等待链表队列中某一个进程唤醒,然后把临界资源分配给它
}
}
}