信号量原理的总结

谈到信号量,大多数会立马想到PV操作,这个用荷兰语表示的专有名词。

对比前面我们说的,四种实现临界区互斥的软件设计算法,PV操作更加的优良,现在这里就将对其原理进行详细的解释分析,以及如何在题目的场景中使用,包括算法的设计和问题的分析。

http://blog.csdn.net/u011240016/article/details/52628467

首先,信号量机制我们着重谈两个设计思路,核心相同,设计有细微的差别,但不要被名称所迷惑。 
它们是:

  • 整形信号量
  • 记录型信号量

整形信号量

整形信号量表示的是资源的数目S。P操作将减少一个资源,V操作将增加一个资源(即释放一个资源,因为资源不会被操作生出来,只会因别人不用而释放)。 
这里不用wait和signa这种看着含义更加明显的名词,是因为,P、V可以这么联想:一个进程想利用资源了,就拍拍手,表达自己的意愿,这样就可以将S-1,而当自己用好了,就表示一个V字手势,表示成功了,资源可以收回去了。

void P(S)
{
    S--;
}

void V(S)
{
    S++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

所以,我更加倾向于用PV直接表达PV操作。因为这个图像更加生动。当然,用wait和signal,as you wish, 你开心就好。

我想强调的有一个点在于:S的边界含义,当然S > 0和S < 0的时候都很自然。

问,S = 0时候表示什么意思?

既然S是可用资源的数目,那么S = 0,表示已经没有资源可用了。

在整型信号量中,只要S <= 0,就会不断测试,看何时有资源可用。

也因此,进程会一直等,不遵循让权等待的原则。

更好的做法是,像我们在进程调度中学到的,加一个就绪队列。 
也即,引入:记录型信号量。

记录型信号量 
我们常说这个算法是一种进程同步机制,而不说是互斥机制。 
是因为,使用PV的思路进行进程的互斥访问,非常简单,只需要设置一个mutex,取值仅仅为0或者1即可。而同步的话就复杂一些,采用用到这个记录型信号量。

首先看semaphore的设计:

typedef struct 
{
    int value;
    struct process *L;
} semaphore;
  • 1
  • 2
  • 3
  • 4
  • 5

这里的value和上面的S含义相同,都表示可用的资源数目。

链表L表示的是就绪队列,因此核心仍是PV,只不过如何对待进程,记录型信号量的做法更加完善。解决了让进程忙等的现象。

void P(semaphore s)
{
    s.value--; // 申请资源,边界是value已经为0了,那么现在变-1,表示有一个进程在等待
    if(s.value < 0)
    {
        将此进程加入就绪队列,等待;
        block(s.L);
    }
}

void V(semaphore s)
{
    s.value++;
    if(s.value <= 0)
    {
        将进程P从就绪队列中移出;
        wakeup(P);// 叫醒P,让它起来干活
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里有意思的是,在V的时候,边界为啥是s.value <= 0.

当s.value = 0时,是从s.value = -1时候变过来的,因此,虽然s.value = 0了,还不能忘了把最后一个进程移出就绪队列并叫醒它。

s.value小于等于0时,绝对值表示的是等待进程的数目。 
还需要注意s.value++的时机。如果设计算法时,先判断再加,则s.value < 0即可:

void V(semaphore s)
{
    if(s.value < 0)
    {
        将进程P从就绪队列中移出;
        wakeup(P);// 叫醒P,让它起来干活
    }
    s.value++;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

所以,算法背后是思想,需要仔细体会才能领悟。

以上。

猜你喜欢

转载自blog.csdn.net/he__yuan/article/details/81431633
今日推荐