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);// 消费者在消费完后,增加空闲缓冲区个数 
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值