死锁原理
可以把死锁定义为一组相互竞争系统资源或进行通信的进程间的永久阻塞。
死锁由四个必要条件:
- 互斥
- 占有且等待
- 不可抢占
- 循环等待
死锁预防
本质是试图设计一种系统来排除发生死锁的可能性。
互斥
这个条件不可能禁止。
占有且等待
可以要求进程一次性地请求所以所需地资源,并且阻塞这个进程直到所有请求都同时满足。低效。
不可抢占
当一个进程进行进一步的资源请求时被拒绝,则它必须释放最初占有的的资源。
当一个进程请求一个被占有的资源时,操作系统要求原有进程释放资源,二者进行抢占资源。
后者才能预防死锁。
循环等待
可以定义资源类型的线性顺序来预防。低效。
死锁避免
与死锁预防的区别,死锁避免允许三个必要条件的发生,通过明智的选择,确保永远不会到达死锁点。死锁避免可以允许更多的并发。
方法:
- 如果一个进程请求会导致死锁,则不启动此进程。
- 如果一个进程增加的资源请求会导致死锁,则不允许此分配。
死锁检测
- 死锁检测算法
算法的策略是查找一个进程,使得可用资源可以满足该进程的资源请求,然后假设同意这些资源,让该进程运行直到结束,在释放他的所有资源,然后算法再寻找一个可以满足资源请求的进程。没有被标记的进程就是死锁进程。 - 死锁恢复
- 取消所有死锁进程,最常用;
- 死锁进程回滚到上一个检查点,并重新启动所有进程;
- 连续取消死锁进程,直到不在存在死锁,取消顺序基于最小代价原则;
- 连续抢占资源直到不再存在死锁。
哲学家就餐问题
解决方案:
- 基于信号量:控制进程数量,保证至少能有一个进程可以满足资源需求,然后当他释放资源,其他进程可以依次得到资源。
- 使用管程:同一时刻只有一个进程可以进入管程,这样就能保证先进入的能够得到资源。
Linux内核并发机制
原子操作
避免简单的竞争条件,单处理器上,线程一旦启动原子操作,则从操作开始到结束这段时间内,线程不能被中断。在多处理器系统下,该原子操作针对的变量是被锁住的,直到操作结束。
自旋锁
Linux中保护临界区最常见的技术是自旋锁。在同一时刻只有一个线程能获得自旋锁,其他企图获得自旋锁的线程将一直进行尝试,直到获得该锁。
信号量
二元信号量
计数信号量
读写信号量
允许多个并发的读者,但仅允许一个写者。
屏障
防止编译器对代码的重排序。