12.进程同步与信号量
Posted PacosonSWJTU
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了12.进程同步与信号量相关的知识,希望对你有一定的参考价值。
【README】
1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
2.进程同步: 让进程间的合作变得合理有序;
3.通过 信号量 来实现进程同步 ;
4.操作系统借助信号量实现进程合作,进程走走停停;(进程什么时候停,在哪个地方停特别重要)
【1】进程合作:多进程共同完成一个任务
【例1】司机与售票员例子
司机 | 售票员 |
While(true) 启动车辆;// 等待信号1 正常运行; 到站停车;// 发送信号2 | While(true) 关门;// 发送信号1 售票; 开门; // 等待信号2 |
【例2】生产者消费者
生产者 | 消费者 |
阻塞直到 counter 不等于 BUFFER_SIZE; 生产数据后,counter加1,类似向消费者发送信号; | 阻塞直到counter不等于0; 消费数据后,counter减1,类似向生产者发送信号; |
【2】进程同步
进程同步定义: 需要让进程走走停停,保证多进程合作的合理有序;
【3】信号量语义
1)只发信号还不能解决全部问题;
- 信号只能表示 有 或者 没有;引入信号量表达更丰富的信息;
2)问题描述:
- 当 counter 等于 BUFFER_SIZE 时,生产者1睡眠;
- 当 counter 等于 BUFFER_SIZE 时,生产者2睡眠;
- 接着,消费者消费一条数据,counter减1;
- 此时 counter减1后等于 BUFFER_SIZE 减1,所以消费者会调用 wakeup唤醒生产者1;
- 接着, 消费者循环消费另外1条数据,counter减1;
出现的问题:
- 此时counter减1后等于 BUFFER_SIZE 减2 (因为这是第2次消费),因为不满足 counter==BUFFER_SIZE-1 条件,所以消费者不会唤醒生产者2;
- 显然,counter语义 不足以唤醒所有生产者,所以引入了信号量;
- (counter记录的是空闲缓冲区数量,而无法记录睡眠的生产者数量,所以根据counter语义无法唤醒所有睡眠的生产者)
3)信号量
信号量不仅需要记录睡眠和唤醒,还需要记录当前阻塞的生产者个数等其他信息;
4)信号量开始工作
步骤 | 生产者与消费者执行详情 | 信号量 sem值 |
1 | 缓冲区满,生产者P1执行, P1睡眠,信号量减1;则信号量为-1;(信号量-1表示1个生产者睡眠) | -1 |
2 | 生产者P2执行, P2睡眠,信号量减1;则信号量为-2(表示2个生产者睡眠) | -2 |
3 | 消费者执行1次循环, wakeup唤醒P1后,信号量加1得到-1;(表示1个生产者睡眠) | -1 |
4 | 消费者再执行1次循环, wakeup唤醒P2,信号量加1得到0;(没有生产者睡眠) | 0 |
5 | 消费者在执行一次循环;信号量加1;(表示有1个可用缓冲空间) | 1 |
6 | 生产者P3执行,信号量减1; | 0 |
信号量sem值含义:
- -2: 有2个生产者阻塞,或者欠生产者队列2个单位缓冲空间;
- -1: 有1个生产者阻塞,或者欠生产者队列1个单位缓冲空间;
- 0: 没有生产者阻塞,正常运行;
- 1: 表示还有1个单位的可用缓冲空间;
- 2:表示还有2个单位的可用缓冲空间;
【小结】
- 接下来,生产者与消费者就可以根据 信号量sem 来决定进程同步,或决定多个进程合作的合理有序执行;
5)基于信号量的进程合作
多个进程合作完成一件事,多个进程在执行过程中,执行的推进顺序要合理有序;
具体地,在执行一定程度后,进程根据信号量判断是否停下来等待;
- 5.1)生产者:若信号量等于0或负值,则生产者进程等待,且信号量减1;
- 5.2)消费者:若信号量等于负值,则消费者唤醒一个生产者进程,且信号量加1;
- 若信号量等于0,则消费者正常执行,且信号量加1;
【4】信号量实现
1)信号量定义:一种特殊整型变量,量用来记录,信号用来判断是否睡眠sleep和唤醒wakeup;
2)信号量代码
// 信号量代码
struct semaphore()
// 记录资源个数
int value ;
// 进程阻塞队列(记录在该信号量上等待的进程)
PCB *queue;
// 生产者:消费资源(这里消费资源指的是生产者消费一个空闲缓冲区,或一个数组项)
P (semaphore s)
s.value--; // 消费资源
if (s.value < 0)
sleep(s.queue); // 当前生产者进程睡眠
// 消费者:产生资源 (这里产生资源指的是消费者释放一个空闲缓存区,或一个数组项)
V (semaphore s)
s.value++; // 释放资源
if (s.value <=0 )
wakeup(s.queue); // 消费者唤醒睡眠的生产者进程
【补充】 荷兰语中
- P表示proberen,即test测试的意思,即生产者;
- V 表示 verhogen,即increment,增加资源的意思,即消费者;
【5】用信号量解决生产者消费者问题
1)信号量解决生产者消费者问题的源代码
- 下面源代码有3个信号量
- full:缓存区中数据或内容个数,或已占用的缓冲区个数;
- empty:空闲缓冲区个数;
- mutex:互斥信号量,同时只允许1个进程进入代码;
// 1 用文件定义共享缓冲区
int fd = open("buffer.txt");
write(fd, 0, sizeof(in)); // 写入数据到in位置
read(fd, 0, sizeof(out)); // 从out位置读取数据
// 2 信号量的定义和初始化
semaphore full = 0; // 表示缓冲区中已生产的数据(内容)个数,或已用缓冲区个数;
semaphore empty = BUFFER_SIZE; // 表示空闲缓冲区个数
semaphore mutex = 1; // 互斥信号量,同时只能有1个进程进去;
// 3 生产者
Producer(item)
P(empty); // 生产者先测试empty信号量是否为0(为0表示没有空闲缓冲区)
P(mutex);
// 读取in,把item写入到in的位置上 (生产操作)
V(mutex);
V(full); // 增加数据(内容)个数
// 4 消费者
Consumer()
P(full); // 消费者先测试缓冲区是否存在内容,则没有则阻塞
P(mutex); // 判断是否可以访问文件,mutex是互斥信号量,同时只有1个进程可以访问文件(mutex减1, 获取mutex信号量或锁)
// 读取out,从文件中的out位置读出到item,打印item (消费操作)
V(mutex); // mutex加1,释放mutex 信号量
V(empty);// 消费者在消费完后,增加空闲缓冲区个数
以上是关于12.进程同步与信号量的主要内容,如果未能解决你的问题,请参考以下文章