信号量方法的基本原则:两个或多个进程可以用信号的方法进行协作;进程可以在任何地方停下来以等待收到特定的信号;信号的实现是用一种称为信号量(Semaphore)的特殊变量。

信号量S就是一个特殊变量,包含一个整数值。
在S上可以执行两个原子操作:wait(S)用来接受信号,也称为P操作;signal(S)用来发送信号,也称为V操作。

信号量分类:计数信号量(Counting Semaphore)、二元信号量(Binary Semaphore)、结构信号量(Structure Semaphore),

结构型信号量

结构型信号量数据结构

typedef struct
{
	int value;
	struct process *L;	// L指向一个进程队列
}

wait操作

void wait(semaphore *S)
{
	s.value--;
	if (s.value < 0)
	{
		add this process into S.L;
		block();
	}
}

signal操作

void signal(semaphore *S)
{
	s.value++;
	if (s.value <= 0)
	{
		remove a process P from S.L;
		wakeup(P);
	}
}

如何用信号量实现互斥?

用信号量实现互斥的方法:
(S.value初始值为1)
wait(S);
临界区
signal(S);

在这里插入图片描述
例如:有三个进程P、Q、R欲进入临界区,S.value初始值为1。P进入时,先执行wait(S),此时S.value=0,不满足if条件,则wait结束,P进入临界区。若此时Q想要进入临界区,也需执行wait,此时S.value=-1,满足<0条件,则将进程加入S,L,block阻塞进程,使其等待。同理若R想进入临界区,则有S.value=-2,R进程也阻塞。
此时若P从临界区出来,执行signal,此时S.value=-1,满足if条件,从等待进程中选择一个进入临界区,假设按照FCFS算法,使Q进入。同理Q出临界区后,S.value=0,R进入,R出临界区后,S.value=1,回归初始值。

如何用信号量实现同步?

用信号量实现同步的方法:
S.value初始值为0
在先执行的进程段后面加signal(S)
后执行的进程段前面加wait(S)

在这里插入图片描述
例子:假设有P1和P2两个进程,要求P1进程的A段必须在P2进程的B段之前执行。
(1) 若此时P1先执行,执行完A段后,执行signal(S),此时S.value=1,不满足signal中的if条件,不进行任何操作,结束signal。再执行P2的B段之前,执行wait(S),此时S.value=0,不满足wait中的if条件,不进行任何操作,结束wait,执行B。
(2) 若此时P2先执行,执行B段之前先执行wait(S),此时S.value=-1,满足wait中的if条件,B段进入S.L队列,阻塞。执行P1中的A段,结束后执行signal(S),此时S.value=0,满足signal中的if条件,将B移出等待队列,唤醒B进程。

信号量取值特点
一个信号量的值可以为负数,其绝对值表示有多少个进程等待这个信号量。
一个信号量的值可以为正数,表示有一个或多个被积累下来的唤醒操作。

使用信号量时需要避免的错误
死锁:两个或多个进程无限地等待一个事件,而这个事件只能由这些等待进程之一来产生。
饥饿:无限期阻塞,即进程在信号内无限等待的情况。

参考文献
北京工业大学网课《操作系统原理》金雪云

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐