在操作系统中,不同的进程和线程之间涉及到一个重要的问题就是互斥,即保证对共享数据的正确修改。
基本的思想就是避免多余一个进程或线程(后面统一用进程来代替)同时读写共享数据。
为了解决这个问题,有很多的想法,为了方便说明各自的问题,按照《现代操作系统》中的思路来叙述。
首先是想到的控制进程的执行顺序,提出了两个方法:一个是严格轮转法,一个是Peterson算法。
严格轮转法就是让进程挨个按序执行,进程0做完了让给进程1,依次下去;缺点就是有忙等待,而且进程0可能会被不在临界区的进程1阻塞(因为两个轮流来);
Peterson算法不再严格轮转,不同进程调用的代码相同,传入各自的进程号来争抢,争抢到的一方可以进入临界区;
除了上述软件上的方法,还提出了一个硬件解决方法,设置一个TSL指令,该指令是原子操作(该操作完成前不允许其他指令访问该内存地址),完成测试和设置内存值的操作。这样可以避免出现两个进程同时修改,然后可利用该内存字来仲裁应该调度哪个进程。
上述方法(其实我觉得TSL使用好了不会)都会导致忙等待的问题,而忙等待会导致出现优先级反转(优先级高的等待优先级低的离开临界区,而优先级低的不能被调度离开临界区)。
为了解决忙等待的问题,又提出了增加wakeup和sleep的方法,即典型的生产者-消费者问题;简单的模型存在wakeup或sleep信号丢失,导致进程不能醒来或睡眠的问题。
然后引入了信号量,信号量要求:检查、修改和唤醒/睡眠操作是一个整体的原子操作,这样避免信号丢失的问题;
信号量其实有两个用法,一个是用来做信号量的计数,一个是用来互斥(当为某个值时会让进程睡眠,当为其他值让进程继续);
如果简化地使用信号量,即不使用它的计数功能,只是用互斥,那么可以一个0或1的”互斥信号量“,这样可以用来支持是否让进程睡眠。
整体上看,由硬件支持的原子操作是非常必须的,只有这样才能真正保证”有效地“(忙等待效率不高)实现进程间互斥,使得进程不会同时修改共享数据。