背景
一些概念
- 竞态条件
- 原子操作:不可被打断的操作
- x++都不是原子的。
- 临界区:当前进程访问共享资源,其他进程无法访问改共享资源。(加锁区域)
- 互斥:当一个进程处于临界区访问共享资源,别的进程无法进入。其他进程在外面等待。
- 死锁:相互等待对方的资源。
- progress:前进状态,如果进程阻塞住想进入临界区,最终会前进。
- 有限等待:如果进程i要进入临界区,处于临界区的进程是有时间限制的,不是无限阻塞的。
- 无忙等待:如果一个进程等待进入临界区,那么在它可以进入之前会被挂起。
- 饥饿:一个进程持续得不到执行,无限期等待。
- 锁:使外人无法访问
- 解锁:打开保护性装置
如何实现临界区代码保护
禁用硬件中断
没有中断,没有上下文切换,因此没有并发。那么进入临界区,关闭中断,离开临界区,开启中断。
产生的问题:
- 一旦关闭,整个系统都会停下来。
- 临界区如果很长,对系统影响很大。
- 多CPU的情况,中断机制屏蔽一个CPU,其他CPU继续可以产生中断。也就无法解决互斥问题。
基于软件的解决方法
peter算法
更高级的抽象
利用硬件原子操作-加锁
test and set、swap都被封装机器指令,原子操作,不能被中断
lock中value=0可以进入临界区,value=1不可以进入,自旋。退出时将value置为0.
-
忙等待的锁
就像上面test and set实现的锁一样,线程在等待的时候消耗cpu。适用于临界区执行时间非常短的情况。 -
无忙等待
将等待进入临界区的进程放入就绪队列,不占用CPU资源,将CPU让给其他进程。 等临界区执行完会去通知就绪队列。适用于临界区执行时间比较长的情况。
以上是基于exchange原子操作的同步情况。第一个进程将key改为0,之后的进程就无法进入,第一个执行完再把key改为1.