注意:一个进程从运行态变成阻塞态是主动的行为;而从阻塞态变成就绪态是被动的行为,需要其他相关进程的协助。
对临界资源的访问必须互斥(就是你正在访问那么我就不能访问了,我正在访问你就不能访问了)的进行。
两个进程无法交替进入临界区(违背“空闲让进”):指的是当P0进入临界区又回来后,会将turn置为1,此时临界区是空闲的,允许P1进入临界区,但是问题来了,P1暂时不想进入临界区,而P0现在又想进入临界区了,可是turn现在为1只允许P1进入,导致P0不能进入,所以违背了“空闲让进”原则。
可能两个进程同时进入临界区(违背“忙则等待”):即检查对方的flag是否为TRUE和切换自己的flag为TRUE前有一段时间,可能发生结果都检查通过,问题是检查和修改操作要分两次完成而不能一次完成。P0现在想要进入临界区了,在进入之前,先检查P1是否在临界区,发现P1不在临界区它的flag为FALSE,表示此刻P0可以进入临界区,然后P0就准备将自己的flag置为TRUE表示正在临界区,但是问题来了,在P0修改成功之前,P1现在也想要进入临界区,检查P0的flag为FLASE(因为还没有修改成功),然后P0就认为自己被允许进入临界区,所以也修改自己的flag,最后就会导致两个进程P0和P1同时将自己的flag修改为TRUE,发生同时进入临界区的问题,违背“忙则等待”原则。
两个进程谁也不能进入临界区(“饥饿”现象):进程P0现在想要进入临界区,所以将自己的flag标志置为TRUE,然后检测P1进程的标志,这个时候(即未检测到P1状态之前),P1也想进入临界区,所以也将自己的flag标志置为TRUE,然后也检测P0的标志,问题来了,P0发现P1标志为TRUE,P1也发现P0标志为TRUE,都互相谦让,然后都不能进入,就会产生“饥饿”现象。
利用flag解决临界资源的互斥访问并且利用turn解决“饥饿”现象(是算法一和算法三的结合):P0将自己的flag标志设置为TRUE表示自己想要进入临界区,然后再设置turn=1表示允许P1进程进入临界区,检测只有当P1的flag标志为TRUE并且turn=1时表示P1已经在临界区,P0进程只能等待,若P1不想进入临界区,即它的flag标志为FALSE,则P0可以进入临界区。
为每个临界资源设置一个共享布尔变量lock,表示资源的两种状态:true表示正被占用,初值为false表示未被占用。在进程访问临界资源之前,利用TestAndSet指令检查和修改标志lock;若有进程正在占用临界区,则反复检查,直到进程退出。
若P2先执行,在运行y语句之前就会执行P(S)检查,那么由于P1进程并没有执行,则S为0,执行P操作就会把进程P2阻塞,放入阻塞队列;当P1进程中的x语句执行完成后,执行到V(S)操作,把P2从阻塞队列中放回到就绪队列,当P2得到处理机时,得以继续执行。
当没有进程进入临界区时,任意一个进程进入临界区,就要只需P操作,把S的值减为0,然后进入临界区;当有进程存在于临界区时,S的值为0,再有进程要进入临界区,执行P操作时将会被阻塞,直至临界区的进程退出,这便实现了临界区的互斥。
dad()和daughter()、mom()和son()必须连续执行,也只能在女儿拿走苹果或儿子拿走橘子后才能释放盘子,即V(plate)操作。
这里的写进程优先是相对而言的,也成为读写公平法,即读写进程具有一样的优先级。
该算法存在以下问题:当五名哲学家都想要进餐并分别拿起左边的筷子时(都恰好执行完成wait(chopstick[i]);)筷子已被拿光,等到他们想要再拿起右边的筷子时(执行wait(chopstick[(i+1)%5]);)就全被阻塞,因此出现了死锁。
为了防止死锁发生,可以对哲学家进程添加一些限制条件,比如至多允许4名哲学家同时进餐;仅当一名哲学家左右两边的筷子都可用时,才允许他抓起筷子;对哲学家顺序编号,要求奇数号哲学家先拿左边的筷子,然后再拿右边的筷子,而偶数号哲学家刚好相反。下面的算法是第二种方法:当一名哲学家左右两边的筷子都可用时才允许抓起筷子。
(1)在T0时刻系统是安全的。因为存在安全序列P2,P1,P3。 ①当前可用磁带机为3台,把这3台分配给P2以满足最大需求,P2结束并归还资源后,系统有5台磁带机可用; ②接下来将5台磁带机分配给P1使用以满足最大需求,P1结束并归还资源后,系统有10台磁带机可用; ③最后分配7台给P3使用,P3也能顺利完成。(2)若在T0时刻后系统分配1台磁带机给P3,系统剩余资源为2,此时系统进入不安全状态,因为找不到安全序列。 ①把剩下的2台磁带机分配给P2,这样P2完成后只能释放4台磁带机,既不能满足P1又不能满足P3,彼此都在等待对方资源,陷入死锁。