1. 分布式互斥算法的概念

分布式互斥算法,就是在分布式系统中,通过某种消息传递算法来决定哪个进程可以访问临界区资源(如CPU等)。

如何去评价分布式互斥算法是否达到了这个目的呢?于是就定义出了三个算法指标:

  • ME1 安全性
    至多一个进程在使用临界区,不会有多个进程同时在临界区执行;
  • ME2 活性
    不会发生死锁、饿死现象;
  • ME3 公平性
    进程顺序执行(FIFO),先到先进。

能满足这三个条件的算法,就是一个良好的分布式互斥算法。

2. Lamport 算法

某个进程 pip_ipi 请求临界区时,会先向所有其他进程发送请求Request(ti,pi)Request(t_i,p_i)Request(ti,pi),其他进程将这个请求保存到本地请求队列里。

其他进程(接收到请求的进程)如果收到了一个请求,那么会按请求中打上的时间戳tit_iti将请求加入到本地请求队列里,然后返回一个Reply(tj)Reply(t_j)Reply(tj)tjt_jtj是指进程 pjp_jpj 收到进程 pip_ipi 请求时的时间戳。

pip_ipi 发送了请求以后进程就等啊等啊,直到:

  1. 进程 pip_ipi 收到了所有其他进程返回的Reply(t∗)Reply(t_*)Reply(t),显然t∗>tit_*>t_it>ti,因为其他进程一定是在接收到 pip_ipi 请求后才打的时间戳(我发了消息后,对方才会回复的消息);
  2. 此时本地请求队列里tit_iti在队头,其他t∗t_*ttit_iti后面。

那么此时进程 pip_ipi 就可以进入临界区执行了。在进程 pip_ipi 退出临界区时从本地队列里删掉自己的请求 Request(ti,i)Request (t_i,i)Request(ti,i),再带着时间戳发一个ReleaseReleaseRelease广播,通知所有队列都可以删掉请求了。

Lamport算法起作用的基础很明显是要满足M3的,也即请求的存储要求FIFO,如果非FIFO的话,连续发两个请求返回值的顺序可能就会乱了。

如下图:
下面的进程在时刻222给请求A打了一个时间戳,在时刻444给请求B打了一个时间戳,但回复B却优先与回复A返回,进程不满足进入临界区的条件,算法就卡死在这里了。
在这里插入图片描述

3. Ricart-Agrawala 算法

Lamport 算法的不足之处在于:

  • 没有申请临界区资源的其他进程是没必要知道申请了临界区资源的进程是否释放(ReleaseReleaseRelease操作)了临界区资源的。

所以ReplyReplyReply操作没必要进行的太早,早回复了也进不去临界区,因此不如等到 ReleaseReleaseRelease 后在ReplyReplyReply

针对上述问题,提出了Ricart-Agrawala 算法进行了改进。

首先,某个进程 pip_ipi 请求临界区时,还是会先向所有其他进程广播请求Request(ti,pi)Request(t_i,p_i)Request(ti,pi),其他进程将这个请求保存到本地请求队列里。

其他进程(接收方)收到请求时:

  • 如果自己没有请求临界区,也没有在临界区执行,那就直接ReplyReplyReply
  • 如果自己正在请求临界区,但是接收方发出的请求的时间戳大于请求消息的时间戳时,也直接ReplyReplyReply
  • 如果不是上述两种情况,也就是说,接收方收到这个请求消息之后,发现自己需要比这个请求更早的进入临界区,那就先不对它ReplyReplyReply,而是记录本地的一个数组RD[pi]=1RD[p_i]=1RD[pi]=1,表示延迟回复进程 pip_ipi 一个请求。

同样的,一个进程只有在收到其他所有进程的ReplyReplyReply后,才能进入临界区执行。退出临界区时要按照RDRDRD数组中的记录,将延迟的ReplyReplyReply发送出去。

可以看出来,Ricart-Agrawala 算法去掉了 ReleaseReleaseRelease 消息,也就是说,当一个进程退出临界区之后,通知其他进程可以进入临界区的信号不再特意通过一个ReleaseReleaseRelease信号去通知了,而是通过补全延迟的ReplyReplyReply信号使其他进程满足进入临界区的条件(此时上一个进程已经释放了临界区资源),然后直接进入临界区就可以了。

4. 算法分析

对于Lamport 算法和Ricart-Agrawala 算法来说,最重要的区别就是Ricart-Agrawala 算法在退出临界区时不再发出ReleaseReleaseRelease消息。下面我们分别来证明一下这两个算法满足了ME1、ME2、ME3。

  1. ME1
    Lamport:如果两个进程 pip_ipipj,(i≠j)p_j,(i\neq j)pj,(i=j)能同时进入临界区,那么这两个进程必须已经互相向对方发送了ReplyReplyReply消息。但是,因为<ti,pi><t_i, p_i><ti,pi>对是在队列中排序的,所以ReplyReplyReply一定是有先后顺序的,只有当某个进程从临界区退出时广播了ReleaseReleaseRelease信号后,另一个进程才能进入临界区。
    Ricart-Agrawala:如果两个进程 pip_ipipj,(i≠j)p_j,(i\neq j)pj,(i=j)能同时进入临界区,那么这两个进程必须已经互相向对方发送了ReplyReplyReply消息。但是,因为<ti,pi><t_i, p_i><ti,pi>对是在队列中排序的,所以ReplyReplyReply一定是有先后顺序的,只有当某个进程从临界区退出后才会发送之前延迟的ReplyReplyReply信号,另一个进程才能获得ReplyReplyReply进入临界区。
  2. ME2
    Lamport:假设进程 pip_ipi 在临界区中执行完毕,那么在它离开临界区时,会广播ReleaseReleaseRelease信号,该信号相当于通知其他进程临界区已被释放,此时新的进程就可以进入临界区,无死锁。
    Ricart-Agrawala:假设进程 pip_ipi 在临界区中执行完毕,那么在它离开临界区时,会发送被延迟的ReplyReplyReply信号,其他进程得到ReplyReplyReply信号后就满足了进入临界区的条件,便可以进入临界区执行,无死锁。
  3. ME3
    Lamport:进程 pip_ipi 先广播了进入临界区的请求 rir_iri,然后进程 pjp_jpj 又广播了进入临界区的请求 rjr_jrj。在理想条件下,会先处理 pip_ipi 的请求,再处理 pjp_jpj 的请求。但由于消息传递有延迟, pjp_jpj 的请求可能会先于 pip_ipi 的请求到达服务器,所以服务器会先处理pjp_jpj 的请求。
    Ricart-Agrawala:进程 pip_ipi 先广播了进入临界区的请求 rir_iri,然后进程 pjp_jpj 又广播了进入临界区的请求 rjr_jrj。假如pjp_jpj先进入了临界区,则说明 rjr_jrj 的时间戳大于 rir_iri,这与事实相悖。因为Ricart-Agrawala算法会通过 rir_irirjr_jrj 的时间戳来判断应该立即ReplyReplyReply还是延迟ReplyReplyReply,这样就可以确保先发送的请求 rir_iri 先得到ReplyReplyReply进入临界区。
Logo

更多推荐