操作系统课程刚学完了信号量机制,通过写这篇文章梳理巩固我自己对于信号量机制的理解,同时记录对一道例题的思考过程,希望能对信号量机制的运用有更加深刻的印象。
信号量机制
我们从信号量机制的设立意义开始思考,操作系统分时复用,多个进程并发运行,这些进程之间主要存在两种关系。
- 一种是计算机存在一些临界资源公共资源,不同进程对其的访问应当是互斥的,这些进程之间是互斥竞争关系。
另一种是当多个进程协作完成作业时,这些进程之间由于一些特定因果关系存在一定的先后关系,此时进程间为同步协作关系。
我们希望并发运行的多个进程能够有条不紊的运行,如果获取不到需要的资源或者因为因果关系得不到满足需要阻塞自己,在需求可以被满足的时候被唤醒,那么必须引入进程同步机制。
信号量机制就是一个可行的卓有成效的进程同步技术。进程之间使用一个公共信号来表示资源的数量。
可以理解为将资源抽象为信号量,举个例子当一个进程想要使用某个临界资源时,先在信号量大于 0获取到资源对应的信号量即可以使用资源,获取到信号量后将信号量减一,如果资源数量≤ 0 了其他进程就不可使用了需要被堵塞,这样就确保了互斥访问。
进程不直接接触临界资源,而是先获取信号量,再去获取资源,实现了进程同步! 不过这也需要确保不同进程对信号量的修改是互斥的才有意义,否则我们的信号量也不可靠,
对于信号量的操作采取原语指令原子操作,即执行时不可被打断,这样能够保证某个进程在修改信号量的时候其他进程不可以对其进行修改。
对信号量的操作
- P (wait) 操作,获取某个信号量,如果没有需要的资源会自行堵塞直到被 V 操作唤醒。获取的是互斥关系的信号量指锁定了对应的临界资源!获取同步关系的信号量对应着临界资源的数量减少。
- V (signal) 操作,释放了某种资源即释放了信号量。这也是信号量机制实现的关键部分!
在题目解答里面有更具体的说明。
题目
有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()]操作,以实现上述过程的同步。要求写出完整的过程,并说明信号量的含义和初值。
解答
解决信号量机制涉及到的问题最重要的是两个过程。
- 关系分析,就是找出多个进程间的关系。
- 信号量的设置以及在合适的位置加入对应的操作。
关系分析
第一步,将 A 和 B 两位同学的动作过程理解为 2 个不同的进程!我们去思考进程之间的关系。
第二步,思考:有哪些临界资源?即不能同时使用的资源?A 信箱,B 信箱。
第三步,进程之间有无因果同步关系?
试着从 A 和 B 的动作中去分析关系。想想 A 和 B 有什么行为(原子行为)。
A:从 A 信箱中拿信,阅读,写回信,投信到 B 信箱。
这要看就很清楚了,A 拿信和投信显然和 B 存在同步关系,显然阅读和写回信和 B 进程没什么关系,自己可以独立完成,这里就不涉及到同步关系。
- A 只有在 A 信箱中有信时才可以从 A 信箱中拿信。B 同理。
- 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的邮箱里
}
}