进程同步与信号量

进程合作

进程合作是指多个进程共同完成一个任务

现实生活中的实例:

#司机
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);
}
posted @ 2021-07-20 11:16  mhp  阅读(72)  评论(0)    收藏  举报