进程同步与信号量
进程合作
进程合作是指多个进程共同完成一个任务。
现实生活中的实例:
#司机
while(true):
#启动车辆
#正常运行
#到站停车
#售票员
while(true):
#关门
#售票
#开门
如果两个过程间完全不知道对方的存在,就会产生问题,例如:司机在正常运行时,售票员开门了🤦♂️🤦♂️
因此需要一个信号推动着两个过程的前进;程序如生活,因此也需要类似的信号推动OS进程的前进,以促进进程间的合作
生产者——消费者实例
引入一个常见的事例,生产者-消费者
有一段共享数据BUFFER
#define BUFFER_SIZE 10
typedef struct {...} item;
item buffer[BUFFER_SIZE];
int in = out = counter = 0;
有一段生产者-消费者进程
# producer
while(true):
while(counter == BUFFER_SIZE);
buffer[in] = item;
in = (in+1) % BUFFER_SIZE;
counter ++;
# consumer
while(true):
while(counter == 0);
item = buffer[out];
out = (out+1) % BUFFER_SIZE;
counter --;
问题来了🎈:如何让进程走走停停,来保证多个进程的合理有序?(进程同步,别闹幺蛾子🤷♀️)
其实也很容易想到,两个极端条件👏:
- buffer满,生产者睡眠,因为再生产就溢出来了嘛,消费者消费一个后,发出一个信号,再唤醒生产者
- buffer空,消费者睡眠,因为再消费也没钱了,生产者生产一个后,发出一个信号,再唤醒消费者
# producer
while(true):
if(counter == BUFFER_SIZE)
sleep();
counter ++;
if(counter == 1):
wakeup(consumer)
# consumer
while(true):
if(counter == 0)
sleep();
counter --;
if(counter == BUFFER_SIZE-1)
wakeup(producer)
这种方式对ONE_TO_ONE还行👺,如果有多个生产者一个消费者呢?
假设此时缓冲区已满,时间片轮到P1:
| P1 | P2 | C |
|---|---|---|
| 生产一个item,睡 | ||
| 生产一个item,睡 | ||
| 执行了一次循环,发信号给P1,P1醒来了(进程就绪状态) | ||
| 又执行了一次循环,无法唤醒P2(正常来说buffer有空余,P2也要醒来) |
因此可以看出,单独的counter(信号)不足以区分到底有多少个生产者,只是等待信号、发信号来唤醒和睡眠一个进程是不够的,那还有什么骚操作呢?🤦♂️
信号到信号量的进化
需要一个量来记录
如果我们有个值能记录2Producer在等待:
| P1 | P2 | C |
|---|---|---|
| 生产一个item,记录1 producer在等待,睡 | ||
| 生产一个item,记录2 producer在等待,睡 | ||
| 执行了一次循环,发现2 producer在等待,发信号给P1,P1醒来了(进程就绪状态) | ||
| 执行了一次循环,发现1 producer在等待,发信号给P2,P2醒来了(进程就绪状态) |
这个值就是信号量(有量的信号),用来记录一些信息,并根据这些信息决定是睡眠还是唤醒,常用sem表示👍👍👍
信号量开始上场
设想场景,buffer满了,对应sem=0,没有资源可用了
| P1 | P2 | C | sem |
|---|---|---|---|
| 生产一个item,记录1 producer在等待,睡 | -1(欠一个资源) | ||
| 生产一个item,记录2 producer在等待,睡 | -2 | ||
| 执行了一次循环,发现2 producer在等待,发信号给P1,P1醒来了(进程就绪状态) | -1 | ||
| 又执行了一次循环,发现1 producer在等待,发信号给P2,P2醒来了(进程就绪状态) | 0 | ||
| 又又...执行了一次循环, | 1(有一个空闲资源) |
可以看出用信号量控制进程的唤醒、挂起真是稳得很👍
信号量的程序实现
现在处理并发任务的信号量都是大佬封装好了的,因为涉及阻塞队列的PCB,所以需要在OS内核态运行相关代码
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){
// 有至少一个进程在等待资源
wake_up(s.queue)
}
}
信号量解决生产者——消费者问题
信号量P/V函数中0值代表着阻塞:
- P 消费资源
- V 生产资源
// 定义共享缓冲区
int fd = open("buffer.txt");
write(fd, 0, sizeof(int));//write in
write(fd, 0, sizeof(int));//write out
// 已经生产内容的个数
semaphore full = 0
// 存在空闲缓冲区的个数
semaphore empty = BUFFER_SIZE
// 互斥信号量,只能有一个进程进入
semaphore mutex = 1
// Producer
Producer(item){
// 空闲缓冲区为0
P(empty);
P(mutex);
//读入in,将item写入到in的位置上
V(mutex);
V(full);
}
// Comsumer
Comsumer(){
P(full);
P(mutex);
//读出out,将item写出到out的位置上
V(mutex);
//增加空闲缓冲区的个数
V(empty);
}
爱生活、爱编程

浙公网安备 33010602011771号