操作系统信号量机制

4.2k words

操作系统信号量机制

信号量机制简介

进程互斥的四种软件实现方式:单标志、双标志先检查、双标志后检查、peterson算法 进程互斥的三种硬件实现方法:中断屏蔽法、TS/TSL指令、Swap/XCHG指令

上述所有方法都无法解决让权等待

迪杰斯特拉提出一种信号量机制。用户进程可以通过使用操作系统提供一对原语对信号量进行操作,从而很方便的实现进程同步互斥

信号量是一个变量用于记录系统资源数量,可以是整数,也可以是一个较为复杂的记录型信号量。 一对原语:wait(S)signal(S)/P(S)V(S)

P、V操作底层实现

整数型信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
int S = 1;//初始化信号量S,表示可用资源数量
void wait(int S){//P操作原语,
while(S <= 0);//资源不够就一直等待

//这里可能存在忙等的情况

S=S-1;//资源足够就占用资源
}


void signal(int S){//V操作原语
S=S+1;//使用完资源释放资源

}

void process01(){
wait(S);//进入,申请资源
使用资源;//临界区,使用资源
signal(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;

void wait(semaphore S){//P操作
S.value--;
if(S>value < 0){
block(S.L);//资源数不够使进程进入紫色太,并将其挂到信号量S的等待队列中
//block将程序挂到队列中解决了整数型信号量忙等的缺陷
}
}

void signal(samephore S){
S.value++;
if(S.value <=0){//释放资源,还有别的进程在等待队列中则唤醒队列中的一个进程
wakeup(S.L);
}
}

信号量机制实现同步互斥

实现进程互斥

  1. 分析并发进程关键活动,划定临界区samephore mutex=1;
  2. 设置互斥信号量mutex,初值为同时能够进入临界区的进程数量一般初值为1
  3. 进入临界区P(mutex)申请资源
  4. 退出临界区V(mutex)释放资源
  5. PV操作必须同步出现,缺少P操作无法保证互斥访问,缺少V操作会导致资源不被释放永不唤醒

实现进程同步

同步要求:各程序需要并发有序地推进 1. 分析在什么地方需要实现一前一后 2. 设置同步信号量 3. 在“前操作”之后执行V(S) 4. 在“后操作”之前执行P(S)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

typedef struct{
int value;
struct process *L;
} semaphore;

semaphore S = 0;
void p1(){
···
V(S);//限制性的程序进行v操作,信号量+1后执行的程序才能接着进行。
···
}

void p2(){

}
#### 实现进程前驱 1. 画出前驱图,将配一对前去关系看作一对同步关系 2. 为每一个前去关系设置同步互斥量 3. 在“前操作”之后执行V(S) 4. 在“后操作”之前执行P(S) ### 经典同步互斥问题 #### 生产者消费者问题 生产者、消费者、n个缓冲区

缓冲区必须互斥访问 缓冲区没满生产者放入 缓冲区不空消费者消费

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

typedef struct{
int value;
struct process *L;
}semaphore;

semaphore mutex;
semaphore full;
semaphore empty;


producer(){//生产者先进行生产,缓冲区有空余将产品放入
生产产品
P(mutex);//申请进入缓冲区
V(full);//full的后操作
将产品放入缓冲区
V(mutex);
P(empty);//empty的先操作
}

consumer(){
P(mutex);//申请缓冲区
V(empty);
消费产品;
P(full);
V(mutex);
}

吸烟者问题

三个烟鬼,各自持有三种卷烟材料A,B,C中的两种,现有一材料提供者provider会在一个桌子上轮流放置一种材料,烟鬼得到相应的材料后可合成成品烟然后吸食,现使用信号量机制解决烟鬼抽烟问题

对桌子的访问需要互斥的访问,放置、取走材料需要同步的进行。烟鬼抽完烟后需告诉厂商自己已经抽完,可以进行下一次取得材料的操作。

1
2
3
4
5
6
7
8
循环:
放置A材料->烟鬼1取材料+吸烟
or
放置B材料->烟鬼2取材料+吸烟
or
放置C材料->烟鬼#取材料+吸烟

返回完成信息
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
semaphore A=0//材料信号量
semaphore B=0
semaphore C=0
seamphore finish=1;//完成信号量,用于互斥的访问桌子
int i=0;//用于实现轮流抽取

provider(){
while(1){

P(finish);//P操作要放到后操作之前
if(i%==0){
放入材料A;
V(A);//V操作要放到前操作之后

}
else if(i%==1){
放入材料B;
V(A);//V操作要放到前操作之后

}
else if(i%==2){
放入材料C;
V(A);//V操作要放到前操作之后

}
i++;
}

}

MA(){
if(A.value==1){
P(A);
取走材料A,卷烟抽烟;
V(finish);
}
}

MB(){
//to do
}

MC(){
//to do
}

读者写者问题

两种进程,读者,写者进程,要求: 1. 可以多个读者同时读取 2. 仅一位写者在同一时刻可进行写入 3. 写者操作之前需要所有读者退出文件

1
互斥关系:写者-读者、写者-写者、同时读取不存在互斥关系
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
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
semaphore mutex=1;
semaphore Rnums=1;//使Rnum可以互斥的访问
int Rnum=0;//记录读者数量
semaphore w=1;//用于防止写进程产生饥饿
//防止饥饿的信号量放到程序最外围进行判断,不会出现因为一个读操作产生的锁始终锁住程序导致写进程饥饿。
Write(){
while(1){
P(w);
P(mutex);
写文件
V(mutex);
V(w);
}
}

Read(){
while(1){

if(==0){//第一个读进程进行加锁
P(mutex);
}
Rnum++;
//若一个读进程刚刚进行P操作但是没有Runm++就跳转到另一个读进程,另一个读进程会产生死锁
//只要能够做到P(mutex)和Rnum++可以做到`一气呵成`就能够解决该问题,这里再一次引用一个互斥信号量对Rnum进行互斥访问便可解决
读文件;
Rnum--;
if(i==0){
V(mutex);
break;
}
}
}

Read_plus(){//但Write进程会出现饥饿问题
while(1){
P(Rnums);
if(==0){//第一个读进程进行加锁
P(mutex);
}
Rnum++;
V(Rnums);
读文件;
Rnum--;
if(i==0){
V(mutex);
break;
}
}
}
Read_plus(){//但Write进程会出现饥饿问题
while(1){
P(w);
P(Rnums);
if(==0){//第一个读进程进行加锁
P(mutex);
}
Rnum++;
V(Rnums);
V(w);
读文件;
Rnum--;
if(i==0){
V(mutex);
break;
}
}
}


需要一气呵成的操作应该想到使用互斥信号量的P、V操作

哲学家进餐问题

一个圆桌上有五名哲学家围坐一桌,每名哲学家不是在思考就是在吃饭,但桌子上只有两个相邻的哲学家之间才有一根筷子,哲学家想要进行就餐必须同时拿起左右手边的两根筷子。

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
semaphore chopsticks[5]={1,1,1,1,1};

Pi(){
while(1){
p(chopsticks[i]);
p(chopsticks[(i+1)%5]);
吃饭;
V(chopsticks[i]);
V(chopsticks[(i+1)%5]);

}
}
//倘若五位哲学家同时拿起一侧的筷子则会出现死锁现象
//可以添加一些限制条件就能够避免死锁
//1.最多允许四位哲学家同时吃饭
//2.奇数号的哲学家可以先拿左筷子,偶数号哲学家先拿右边的筷子。

//使哲学家要吃面的话一气呵成拿起两只筷子;
semaphore chopsticks[5]={1,1,1,1,1};
semaphore mutex=1;
Pi(){
while(1){
P(mutex);
p(chopsticks[i]);
p(chopsticks[(i+1)%5]);
V(mutxe);
吃饭;
V(chopsticks[i]);
V(chopsticks[(i+1)%5]);

}
}

Comments