软件实现
单标志法(不安全)
算法描述:进入临界区只做“检查”,不“上锁”<br>存在问题:不遵循“空闲让进”原则。因为资源必须被两个进程轮流访问
不安全场景:轮到 P0 进程访问资源,但 P0 没有访问,P1 想要访问资源就只能等待 P0 访问后才能访问
双标志先检查(不安全)
算法描述:先检查对方访问资源的意愿,如果对方不访问资源,则自己一定访问资源<br>存在问题:违反“忙则等待”原则。原因在于,进入区的“检查”和“上锁”两个处理不是一气呵成的<br>
不安全场景:P0 和 P1 都没想访问资源。P0 和 P1 在进入区发现对方没有访问资源的意愿,结果 P0 和 P1 都进入临界区
双标志后检查(不安全)
算法描述:先表名自己需要访问资源的意愿,再在进入区检查对方的意愿<br>存在问题:违背了“空闲让进”和“有限等待”。存在饥饿问题<br>
不安全场景:P0 和 P1 都表达的访问资源的意愿,所以在进入区检查对方到对方的意愿后会等待对方修改意愿。导致饥饿(不是死锁,因为线程均未进入临界区)
Peterson 算法(安全,但不完美)
算法描述:结合双标志法、单标志法的思想。如果双方都争着想进入临界区,进程会尝试谦让<br>问题:违反“让权等待”原则。如果某个进程无法进入临界区,那么它不会主动放弃 CPU 并进入阻塞态<br>
turn 变量表示当前持有最高优先级的进程
不存在不安全场景。只是因为违反“让权等待”原则而显得不是那么高效
PS:如果双标志先检查和双标志后检查能实现“检查”和“上锁”能一气呵成(保证其原子性)那么算法就没有安全隐患
硬件实现
中断屏蔽法(安全,但不完美)
使用“开/关中中断”指令保证一个 CPU 上运行的进程不会被切换。单处理器失去异步性即可保证资源的串行访问
限制
仅适用于系统进程上,而非用户进程上。“开/关中断”指令应该由内核控制,而不应该由用户控制
仅适用于单处理器。因为 2 号 CPU 并不会因为 1 号 CPU 关中断而不再切换进程,所以计算机整体上没有失去异步性
TestAndSet / TS 指令 / TSL 指令(安全,但不完美)
算法描述:TestAndSet 方法实现上锁和检查一气呵成。如果 old 为 true 表示上锁失败<br>
TSL 指令是用硬件实现的,执行的过程不允许被中断,只能一气呵成
误区
Q:从上述代码来看,如果 old 为 false 时,多个进程的 TS 方法可能会均返回 true 并进入临界区
A:实际上 TS 指令的执行是由硬件的逻辑电路实现的,为理清逻辑才用代码形式说明 TS 的执行流程。即理论上不存在多个进程同时占用 TS 逻辑电路的情况,也即 TS 方法只能被串行执行。下面的 Swap 指令同理
缺点:违背“让权等待”<br>
Swap 指令 / XCHG 指令 / CAS(安全,但不完美)
算法描述:原理和 TS 相同,实现检查并上锁的原子操作。old 初值为 true 表示上锁意愿,通过 swap 交换 old 和 lock,如果 old 能换出 false,表示锁之前未被占用并成功被 old 交换值(old 的值为 true,所以成功交换值表示 lock 被设为 true,即上锁)<br>
缺点:违背“让权等待”
信号量(安全,且完美)
概念
wait原语(P 操作 )和 signal原语(V 操作)实现阻塞进程和唤醒进程。通过合理调用 PV 操作,不断阻塞和唤醒进程实现进程串行访问资源
整型信号量原语(安全,但违反“让权等待”,还是不完美)
属性:S。表示资源能接受的并发进程数
方法:wait(S)。本质是原语,所以只能被串行执行
方法:signal(S)。本质是原语,所以只能被串行执行
记录型信号量原语(安全,完美)
属性:S。表示资源能接受的并发数;L。表示阻塞队列中想要访问此资源的进程
方法:wait(S)。本质是原语,所以只能被串行执行
方法:signal(S)。本质是原语,所以只能被串行执行
PS:使用信号量实现的同步安全案例可参考经典同步问题