并发:互斥与信号量
一、关键术语
1原子操作:一个或多个指令的序列,对外是不可分的;即没有其他进程可以看到其中间状态或者中断此操作。
2.临界区(critical section):是一段代码,在这段代码中进程将访问资源;只能有一个进程在此段代码中运行。
3.互斥(mutual exclusion):当一个进程在临界区访问共享资源时,其他进程不能进入临界区访问任何资源。
二、互斥
1.为什么要互斥:因为当多个进程同时运行在临界区对共享资源进行操作时,可能会导致得到非预期结果。
2.互斥的实现
2.1硬件方法
2.1.1 中断禁用
在单处理器机器中,并发进程只能串行执行,所以只要禁止对临界区的中断即可保证互斥。
缺点:导致效率降低;不能用于多处理器机器。
2.1.2 专用机器指令
设计专门的指令,保证指令执行是原子的。
1).比较和交换指令
intcompare_and_swap(int &word, int testval, int newval)
{
intoldval=word;
if(old==testval)word=newval;
returnoldval;
}
compare_and_swap是机器指令,在执行期间不会被中断。
//***
while(compare_and_swap(a,0,1)==1); //初始化a=0
//临界区
a=0;
//***
第一个进程执行时,a=0,指令返回0,所以进程进入临界区,然后a=1,其他进程无法进入临界区。
缺点:使用了忙等待,无法进入临界区的进程一直在执行指令,消耗CPU时间。
2.1.3 信号量(semaphore)
信号量是进程间传递信号的一个整数值。只能进行三种操作,初始化、递减和增加。
1)一个信号量S可以被初始化为非负整数
2)Wait(s)操作使S减1。如果S变成负数,则将执行Wait的进程阻塞,放进S的等待队列中。如果S为正,则继续执行(进入临界区)
3)Signal(s)使S增加1。如果S小于等于0,则说明在S的等待队列中有进程被阻塞,所以将其中一个进程解除阻塞。
开始时,S被初始化为正数,S等于能同时进入临界区的解除进程数,当进入临界区的进程数超过初始值后,执行Wait()的进程进入等待队列,并阻塞(避免了忙等待),S值小于0,其绝对值|s|等于等待队列中进程数。
信号量的实现:
因为信号量也是共享资源,对其操作也需要互斥,可以用cmp&swap实现。
如:
Wait(s)
{
while(compare_and_swap(s.flag,0,1)==1);
s.count--;
if(s.count<0)
{ //阻塞该进程 s.flag=0
}
s.flag=0;//让其他进程通过while
}
Signal类似。
这里也需要忙等待,但是这里忙等待的对象是Wait和Signal,他们的操作相对于临界区来说很短,所以开销可以接受。
总结:
比如临界区C是厕所,信号量S表示同时能进厕所的人数。
不使用信号量时:
第一个人进入C后,其他人只能在外面等待,并且不断询问里面的人好了没。
使用信号量:
增加了房间S和B,要上厕所时,先到S里面那C的钥匙(信号量的初始值即为钥匙总量),拿到的进入C,拿不到的进入B休息室(阻塞)。S每次也只能通过一个人,其他人也需要在S外面忙等待,但通过S的时间远小于C,所以等待时间不长。从C出来后,如果B中有人,就将钥匙给B中的人,即当s.count<=0是解除阻塞进程。
参考资料:操作系统:精髓与设计原理(原书第六版)