挑战408——操作系统(10)——信号量与PV操作

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/redRnt/article/details/83340531

处理同步和互斥的问题,除了用到之前的软件和硬件的方法,用的最多的还是信号量机制
信号量机制是通过定义表示共享资源使用的特殊变量以及两个标准的原语(P操作和V操作),来实现同步和互斥的。
根据信号量的数据类型不同,我们将信号量分为整型信号量和记录型信号量

整型信号量

整型信号量是一种被定义为用来表示资源数的整型量S,其值只能被wait和signal操作。S的初始值被设置为可访问的资源数的数量。当用整型信号量用于互斥的时候,S被初始化为1,表示临界资源不能被多个进程同时访问。wait,signal操作代表表示如下:

//整型信号量
int s;
wait(s){
	while(s < 0){
//若s的初值为1,下面的语句就会使s = 0,表示可用此时没有可用资源数
//所以此刻申请该资源进程就会阻塞,也就是不允许多个进程同时使用
//这就是为什么用于互斥的时候,互斥信号量要设置为1
		s = s - 1;
	}
}//相当于p操作,进程申请一个资源

而对于signal操作:

signal(s){
	s = s + 1;
}//与wait操作相反,表明这个时候释放一个资源

记录型信号量

但是当多个进程需要访问该资源的时候,如何解决呢?
记录型信号量中,包括一个整型的数值S,代表可用的资源数和一个进程链表queue,用于链接所有等待该资源的进程,定义其数据结构如下:

struct semaphore{
	int value;//代表可用的资源数
	struct PCB *queue;//用于链接所有等待该资源的进程
}

信号量只能通过两个原语操作(P,和V)来访问它们,而PV对应的代码操作如下:

/P操作的代码表示
void wait(semaphore s){
	s.value = s.value - 1;//可用资源数减一
//若可用资源数小于0,阻塞该进程,并投入等待队列(注意不是就绪队列)
	if(s.value < 0) block(s.queue);
}


//V操作的代码表示
void signal(semaphore s){
	s.value = s.value + 1;//可用资源数加一
	//若此时s.value <=0,表明在等待队列中有等待该资源的进程被阻塞
	//故将其从等待队列中唤醒,并投入到就绪队列中
	if(s.value <= 0) wakeup(s.queue);
}

显然我们可以得出这样一个结论:P操作意味着释放一个资源,V操作意味着释放一个资源。当s.value <= 0时,其|s.value <= 0|的值代表的是等待队列中等待该资源的进程数。如s.value = -8,那么表示等待队列中有8个进程正在等待这个临界资源,s.value = 0,则表示资源刚好使用完毕。

利用信号量解决互斥问题

看一个程序段:

//程序P1
{
	R1 = COUNT;
	R1 = 1;
	COUNT = R1;
}

//程序P2
{
	R2 = COUNT;
	R2 = 1;
	COUNT = R2;
}

假设两段程序共享一个变量COUNT,那么如果程序并发执行,则可能出现两种结果,COUNT = 6或者COUNT = 7;(就不分析了),但是结果肯定是唯一的。造成这个原因我们之前说过,就是因为P1在访问临界资源count的时候,P2也在访问,导致前后的值不一致。所以解决的方法就是当其中一个进程在访问的时候,将这个变量保护起来。我们就用信号量机制来表示一下;
下面的代码是我假设执行顺序是P1 -> P2.,过程看我的注释。反过来的分析是一样的

semaphore mutex = 1;//设置初始互斥信号量为1

//程序P1
{
	P(mutex);//申请一个资源,此时mutex -=1(即 = 0)
//下面的三句代码是P1的临界区	
	R1 = COUNT;
	R1 = 1;
	COUNT = R1;
	//释放一个资源,此时mutex += 1,即恢复1
	v(mutex);
}

//程序P2
{
	//申请一个资源,此时如果P1在使用,那么mutex = 0进程阻塞
	//下面的代码都不执行。知道mutex的值大于0为止()即P1释放
	//相应资源的时候
	P(mutex);
	//P2的临界区
	R2 = COUNT;
	R2 = 1;
	COUNT = R2;
	//释放资源
	V(mutex);
}

这样,无论按什么样的执行顺序,COUNT的结果都是7,实现了可再现性。
注意,在解决互斥问题的时候,申请(P)的资源最后一定要释放(V)出来,否则后序的进程不能执行

信号量解决同步问题

同步,是指进程间要按照一定的顺序进行。有些程序并不满足并发的条件(比如我们前面举过的例子),因为后面的进程推进必须依赖前面进程的结果。因此必要的时候要控制进程的先后执行顺序。
我们通常用前驱图来表示进程间的同步关系。如下图:
在这里插入图片描述
显然,我们知道P2,P3可以并发执行,但是P2和P4却不可以。我们用PV操作来描述这一过程(我们一开始设置信号量S1 = S2 = S3 = 0)

分析:
对于P1,它是进程的起始,一定要先于P2,P3,P4执行,而P2,P3并发执行,都要用到P1所提供的资源。所以P1才会先执行代码,然后释放两个资源供P2,P4使用.这里有个问题,就是为什么设置信号量为0?因为我们第一步要V(s1),初始值为0,V一下就加1,这个时候才有资源给P2去申请(P)。
对于P2,它先向P1申请到要的资源后,进入临界区运行,然后释放资源给P4继续运行。
对于P3,P4,他们都顺利申请到了想要的资源,但是不再释放资源了,因为已经没有了后续需要使用资源的进程了。
所以,我们可以得出结论,PV操作是成对出现的,有对某个资源的P操作,就一定要对某个资源的V操作

猜你喜欢

转载自blog.csdn.net/redRnt/article/details/83340531