信号量机制

为啥要引入信号量机制,关于实现进程互斥我们有四种软件实现方法,三种硬件实现方法,但是要么就是不能让上锁和检查一气呵成出现问题,要么就是上述所有方法都有的一个通病:不能实现让权等待。那么,就是信号量机制诞生的意义啦。

信号量:一个变量(可以是一个整数,也可以是更复杂的记录型变量),可以用一个信号量来表示系统中某种资源的数量。

用户进程可以通过使用操作系统提供的一对原语来对信号量操作,从而很方便的实现进程互斥,进程同步。

划重点:一对原语:P,V操作=>P(S),V(S),S为调用时传入的参数,是信号量;其中P,V分别可表示为wait和signal

信号量机制–整型信号量

用一个整数型变量作为信号量,这里这个整型信号量只能用于三种情况,初始化,P操作,V操作

wait实现的底层原理

1
2
3
4
5
int S = 1; //初始化
void wait(int S){ //wait原语,相当于进入区
while(S<=0); //如果资源不够就一直等待,会出现忙等,不满足让权等待
S=S-1; //如果资源够,就占用一个资源
}

signal实现的底层原理

1
2
3
4
int S = 1;
void signal(int S){//signal原语,相当于退出区
S=S+1; //用完资源,在退出区释放资源
}

例如:这里有一个进程P0

···

P(s); //进入区,申请资源

//临界区,访问资源

V(s); //退出区,释放资源

···

敲重点!!!整型信号量的缺陷就是不满足让权等待

信号量机制–记录型信号量

先来看看原理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//记录型信号量的定义
typedef struct{
int value; //剩余资源数
Struct process *L; //等待队列
}semaphore;
//某进程需要资源时,通过wait原语申请
void wait(semaphore S){ //传入的必须是semaphore类型的
S.value--;
if(S.value<0){
block(S.L);//如果资源不足,就用block原语转为阻塞态,并加到信号量等待队列的队尾上
}
}
//进程使用完资源后,通过signal原语释放
void signal(semphore S){
S.value++;
if(S.value<=0){
wakeup(S.L);//如果加1后还是小于等于零说明之前有进程被挂在等待队列上了,就使用wake原语唤醒,从等待队列的对头拿出一个进程,将他从阻塞态转为就绪态并分给他资源让他运行
}
}

这个过程要是不理解就写出四个进程,给两个资源自己比划比划,但是要强调一下,不论是P操作还是V操作,不论满不满足资源够用,都要先对value操作。这样就避免了违反让权等待。

用信号量实现进程互斥

1 分析问题,找出临界区

2 设置互斥信号量,初值为1(因为临界区只允许一个)

3 进临界区之前做P操作

4 进临界区之后做V操作

举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
semaphore s = 0;
P1(){
···
P(s);
临界区
V(s);
···
}
P2(){
···
P(s);
临界区;
V(s);
···
}
用信号量实现进程同步

问题来了,什么是同步,那就记好了,同步就是你想让他有顺序,比如谁必须在谁之前执行,注意理解下面第3,4步

1 分析问题,找出哪里需要实现”一前一后”的同步关系(比如代码4必须在代码2后执行)

2 设置同步信号量,初始值为0

3 在前操作之后执行V操作

4 在后操作之前执行P操作

举个栗子

1
2
3
4
5
6
7
8
9
10
11
12
13
semaphore s = 0;
P1(){
代码1
代码2
V(s);
代码3;
}
P2(){
P(s);
代码4
代码5
代码6
}
用信号量实现进程的前驱关系

1 分析问题,画出前驱图,把一堆钱去关系都看成一个同步问题

2 为每一对前驱关系设置同步信号量,初值为0

3 在前操作之后执行V操作

4 在后操作之前执行P操作

举个栗子

前驱关系

如上图就是一个前驱图,说白了就是必须在谁谁谁完成之后,谁谁谁才能运行,比如S1完了,S2,S3才行;S2完了,S4,S5才行;S4,S5,S3完了,S6才行。而每一条箭头连接的两个进程就是同步关系,依次信号量都是0,遵循同步的规则

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45

P1(){
···
S1;
V(a);
V(b);
···
}
P2(){
···
P(a);
S2;
V(c);
V(d);
···
}
P3(){
···
P(b);
S3;
V(g);
···
}
P4(){
···
P(c);
S4;
V(e);
···
}
P5(){
···
P(d);
S5;
V(f);
···
}
P6(){
···
P(e);
P(f);
P(g);
S6;
···
}

因此只要把图画出来就会变得非常简单

-------------本文结束感谢您的阅读-------------