233_进程互斥的硬件实现方法
# 2.3_3_进程互斥的硬件实现方法
各位同学大家好,在上一小节中我们介绍了进程互斥的 4 种软件实现方法,这个小节我们会介绍另外的三种进程互斥的硬件实现方法。那么这个小节的学习过程当中,大家需要注意理解各个方法的原理,并且要稍微的了解各个方法有什么优缺点。
# 中断屏蔽方法
那么首先来看第一种中断屏蔽方法,其实中断屏蔽这种方式,咱们在之前介绍原语的时候也介绍过,它无非就是使用开中断和关中断这两个指令来实现不可被中断这件事情,那么原语不可被中断的特性,其实也是用一这样一组指令来实现的。 在一个进程访问临界区之前,它会执行一个关中断指令,也就意味着执行了关中断之后,进程就不可能再被中断,也就意味着不可能会再发生进程切换的事情。一直到这进程访问完临界区执行开中断指令之后,才有可能会有别的进程被切换上处理机运行并访问这个临界区。
所以这种方式很明显,它的优点就是实现起来很简单高效,并且这个逻辑其实很清晰,然后缺点就是它不适用于多处理机的这种系统。因为关中断指令,只对执行关中断指令的处理机有用,所以如果此时处理机 a 执行了关中断指令,那么就意味着在处理机上面的进程不会被切换,那么这个进程就可以顺利的访问临界区,但是另一个处理对于另一个处理机 b 来说,它其实还是会正常的切换进程。如果说此时在另外的处理机上运行的进程,也需要访问临界区,也用这种方式的话,那么也有可能会发生两个处理机上的两个进程,同时对临界区进行访问的情况,所以这是中断屏蔽方法,不适用于多处理机的原因。
另外一个缺点就是关中断和开中断这两个指令,它的权限特别大,它属于特权指令,需要在内核态下才能运行。所以如果这两个指令能够让用户随意使用的话,会很危险。因此这种方式只适用于操作系统内核进程,不适用于用户进程,只有操作系统内核进程才有权限执行这两个指令,所以这是中断屏蔽方法。
# TestAndSet 指令
TSL 这个指令其实是用硬件来实现的,在执行的过程当中是不允许被中断,只能一气呵成。这个是用 c 语言描述的 TestAndSet 这个指令的一个逻辑。需要强调的是这个只是为了表示一个逻辑,让大家理解他在背后做了一些什么事情,但是这些事情其实都是由硬件来完成的,并且它不可以被中断。
那么第二个硬件实现方式是 TestAndSet 这样一个指令,可以简称为 TS 指令,有的地方有的书上也会把它称为 TestAndSet Lock。也就 TSL 指令。所在咱们的 408 真题当中也出现过 TSL 这样的一个表述方式,所以这两个名称大家都需要注意一下
系统会为临界区设置一个共享变量,布尔型的变量 lock。lock 是用来表示当前临界区是否已经被加锁,是否已经被上锁,如果表它为 true 的话,就表示已经有某一个进程对临界区上锁,如果为 false 的话就是不上锁。
那么 TestAndSet 这个指令他在背后做的事情,首先它会用一个叫做 old 的也是布尔型的变量,用来记录以前 lock 它的值到底是多少,也就是用来记录之前临界区是否已经被上锁了。然后无论临界区是否被上锁,他在记录下来这个值之后,一定会让临界区进入一个加锁,一个上锁的状态,也就是把 lock 值设为 true 之后,再用某种方式返回 old 的这个值。
那么在实际使用 TSL 实现互斥的过程当中就是用这样的方式来实现的。在 while 这个循环当中会不断的执行 TestAndSet 指令。如果说 lock 本来 true,也就是说以前临界区被上锁的,那么它返回来的 old 的值也会为 true。那么 while 这个循环就会一直循环下去,一直到 lock 这个值,被当前访问临界区的进程,在退出区改成了 false,所以就会变成 TestAndSet 返回来的 old 这个值变为了 false,于是之前一直被卡在这儿的进程就可以跳出这个循环,然后正式的开始访问临界区的代码段,一直到访问结束之后再把自己上的锁给解除,所以这是 TestAndSet 指令的一个实现的一个逻辑。
这个地方虽然用了 return,但它实际硬件执行的过程当中,其实就是把 lock 这个值放到了某一个物理寄存器里,然后再把 lock 值覆盖为 true。
所以刚才咱们分析已经分析了 lock 为 false 和 lock 为 true 的情况,这个算法和之前咱们介绍的软件实现的方法相比,其实在双标志先检查和双标志后检查那两个方法当中,导致最后出问题的原因是在进入区当中进行上锁和检查,这两个操作是有可能会分开进行的,并不是一气呵成的,但是如果说用硬件的 TSL 指令来执行的话,那么就可以保证检查和上锁,这两个事情其实是一气呵成的,一边把它上锁,一边把进行检查,把以前的值给返回回来,所以这是它能解决问题的一个原因,这种算这种方式的优点就是实现起来很简单,其实用如果有这样一个指令的话,那么我们的代码不会特别复杂,可以很简洁,不需要像软件的实现方法那样来各种计算推算是否会有异步带来的一些逻辑漏洞。
所以这种方式其实是比起软件解决方法来说是要好得多的,并且它也适用于多处理机环境。当然为什么适用于多处理以及环境有兴趣的同学,大家可以去查一下,涉及到总线相关的一些特性。
但这个方法也有一个缺点,就是他不满足让权等待的原则。可以通过刚才的分析,我们知道当 lock 此时是被上锁的情况下,那么此时想要再进入临界区的另外一个进程,它会一直在 while 循环里被卡住,一直不断的执行 TSL 这个指令。所以它其实是会即使暂时没有办法进入临界区,但是它也会一直占用着 CPU,然后执行这个指令,从而导致这种盲等的这种现象,所以这是它的一个缺点。
# swap 指令
那么在执行了指令之后,会例行检查,以前 lock 这个值是否为 true。如果说 old 的值为 true 的话,那么就说明在之前临界区就已经被上锁了。那么这个循环下去一直到 old 的为 false,那么说明之前临界区是没有被上锁的状态,那么就会跳出这个循环,然后可以顺利的进入临界区,访问这些代码段。
最后我们再来介绍 swap 指令,大家也需要注意一下,另外这两个名称在考试中如果不小心遇到的话,也需要能够知道他说的就是 swap 指令。这个指令和刚才 TSL 一样,它也是用硬件实现并且中间是不允许被中断的。
那么 swap 指令做的事情其实就是交换了两个变量的值,把 a 的值换到了 b,把 b 的值换到 a,这个逻辑并不复杂,那么它在具体的使用当中实现互斥,是这样实现的。
其实表面上看起来 swap 和之前 TSL 是有很大不同,但是逻辑上来看他们所做的事情其实差不多,也就是刚开始他会用一个 old 的这样的变量来记录以前 lock 这个值到底是 true 还是 false。Lock 值和 TSL 里面一样,用来表示当前是否临界区是否已经被上锁。那么 swap 指令对 lock 和 old 的这两个变量进行交换之后,那么以前的 lock 的值会放到 old 的里边,然后 old 本来是 true 的,那么它 true 这个值又会被设置到 lock 这儿,所以其实他做的和 TSL 可以说在逻辑上看是一模一样的。
当然 swap 指令和 TSL 指令在硬件层次可能实现的方式会不太一样。但是我们可以看到逻辑上看,他们俩做的事情其实并没有太大的区别,所以他们的优点和缺点也可以说是一样的,实现简单,但是也适合于多处理机的环境,不过同样和 TSL 指令一样,都不满足让全等待的原则,如果说此时暂时不能进临界区的话,他可能一直被卡在这个循环这儿,占用处理机,然后不断的循环检查进入盲等的状态。
# 小结
这就是这个小节的全部内容,我们介绍了三种,硬件进程互持的硬件、实现方式其中 TSL 和 swap,这两种指令在在逻辑上其实就是做了这样几个事情,大家再结合这个图再来回忆分析一下。那么对于中断屏蔽这种方法来说,它只适用于单处理机系统,并且只能由操作系统内核进程来使用开关中断这两个特权指令。