操作系统课程刚学完了信号量机制,通过写这篇文章梳理巩固我自己对于信号量机制的理解,同时记录对一道例题的思考过程,希望能对信号量机制的运用有更加深刻的印象。

信号量机制

我们从信号量机制的设立意义开始思考,操作系统分时复用,多个进程并发运行,这些进程之间主要存在两种关系。

信号量机制就是一个可行的卓有成效的进程同步技术。进程之间使用一个公共信号来表示资源的数量。
可以理解为将资源抽象为信号量,举个例子当一个进程想要使用某个临界资源时,先在信号量大于 0获取到资源对应的信号量即可以使用资源,获取到信号量后将信号量减一,如果资源数量≤ 0 了其他进程就不可使用了需要被堵塞,这样就确保了互斥访问。
进程不直接接触临界资源,而是先获取信号量,再去获取资源,实现了进程同步! 不过这也需要确保不同进程对信号量的修改是互斥的才有意义,否则我们的信号量也不可靠,

对于信号量的操作采取原语指令原子操作,即执行时不可被打断,这样能够保证某个进程在修改信号量的时候其他进程不可以对其进行修改。

对信号量的操作

在题目解答里面有更具体的说明。

题目

有A、B两人通过信箱进行辩论,每个人都从自己的信箱中取得对方的问题。将答案和向对方提出的新问题组成一个邮件放入对方的邮箱中。假设A的信箱最多放M个邮件,B的信箱最多放N个邮件。初始时A的信箱中有x个邮件(0<x<M),B的信箱中有y个邮件(0<y<N)。辩论者每取出一个邮件,邮件数减1。A和B两人的操作过程描述如下:

cobegin { 
    A {         
        while(True) {             
        从A的信箱中取出一个邮件;             
        回答问题并提出一个新问题;             
        将新邮件放入B的信箱;         
        }     
    }     
    B {         
        while(True) {             
        从B的信箱中取出一个邮件;             
        回答问题并提出一个新问题;             
        将新邮件放入A的信箱;         
        }     
    }
} 
coend

当信箱不为空时,辩论者才能从信箱中取邮件,否则等待。当信箱不满时,辩论者才能将新邮件放入信箱,否则等待。请添加必要的信号量和P、V[或wait()、signal()]操作,以实现上述过程的同步。要求写出完整的过程,并说明信号量的含义和初值。

解答

解决信号量机制涉及到的问题最重要的是两个过程。

  1. 关系分析,就是找出多个进程间的关系。
  2. 信号量的设置以及在合适的位置加入对应的操作。

关系分析

第一步,将 A 和 B 两位同学的动作过程理解为 2 个不同的进程!我们去思考进程之间的关系。
第二步,思考:有哪些临界资源?即不能同时使用的资源?A 信箱,B 信箱。
第三步,进程之间有无因果同步关系?
试着从 A 和 B 的动作中去分析关系。想想 A 和 B 有什么行为(原子行为)。
A:从 A 信箱中拿信,阅读,写回信,投信到 B 信箱。
这要看就很清楚了,A 拿信和投信显然和 B 存在同步关系,显然阅读和写回信和 B 进程没什么关系,自己可以独立完成,这里就不涉及到同步关系。

信号量设置

信号量初始化

我们给两个互斥竞争资源设置互斥信号量 mutexA = 1 mutexB = 1!只有一个资源,只能一个进程使用!

给同步协作关系设置同步信号量,拿信需要的资源是信封量,我们设置为 fullA fullB,根据题意分别设置初值为 x 和 y。投信需要的资源是空位量,根据题意我们设置为 emptyA emptyB,根据题意分别设置初值为 M - x, N - y。

适当的位置加入对信号量的操作

接下来需要考虑的是在什么时候加入信号量的操作。这里我觉得是最简单的一步。只需要在进程处理过程中遇到对于有同步关系的操作时加入信号量操作即可。
比如题目中,从 A 的信箱中取出一个邮件操作:A 的信箱有邮件,wait (fullA) 即可,去邮箱里那涉及到临界资源 A 邮箱, wait (mutexA) ,关键是记得 signal 操作也就是给信号,表示自己释放了某种资源,拿完信后应该有空位出来需要 signal (emptyA),用完信箱需要 signal (mutexA) 。这里释放信号量也是信号量机制的关键部分。

这一步值得注意的地方是先 wait (fullA) 先等有信了,再去锁定邮箱,即 wait (mutexA)。
   A {         
       while(True) {             
       从A的信箱中取出一个邮件;  //A的信箱有邮件,则去邮箱里拿邮件
       回答问题并提出一个新问题; //不涉及到进程同步关系,A进程自己就可以完成
       将新邮件放入B的信箱;  // B的邮箱有空位,把邮件放进B的邮箱里
       }     
   }     

完整答案