死锁与囚徒困境的辩证关系
最近在复习<操作系统>的重修考试o(╥﹏╥)o, 原来准备戴上耳机来一场硬核复习, 但是发现死记硬背没用还浪费生命, 只好假装喜爱这一门学科, 顺带研究了下一些OS的底层原理, 期间通过研究银行家算法原理时发现了一些有趣的现象, 顺便联想到一些哲学思想, 想和大家分享一下: )
首先谈谈OS在预防死锁的时候和进程之间进行了哪些博弈.
操作系统进程死锁是一个古典问题, 由于进程之间的不信任关系, 2个进程有可能形成一种胶着状态, 即循环等待, 无尽的浪费时间. 为啥会出现这种情况呢? 根据作业的逻辑, 一个进程完成一个任务如果需要2个以上的资源, 一定要先后获得所有资源的控制权后才会开始执行, 比如一个RPG游戏进程保存游戏进度的时候需要更新2个文件: 剧情进度文件A和主角当前属性文件B. 游戏获得了文件A的write权限后并不会立即写入A, 而是再去申请文件B的write权限, 但是如果期间B已经被其它恶意软件获得了write权限(因为对OS而言, 只要有进程索要一个不可共享资源的权限时, 只要合法就没有任何理由拒绝), 这时候游戏会一直等待B的占用被释放, 等待的期间自己对A的控制权不会释放(因为释放了就代表了示弱, 进程之间不信任意味着进程之间不能协商!).
以上这个情况就形成了一个单向无尽等待, 真实环境下不会出现这种情况, 因为游戏配置文件是属于游戏的私有物, 其他恶意软件没有权限访问这个文件, 游戏内部更不会出现死锁, 因为内部线程之间可以协商和变通.
真实情况下的死锁是循环等待造成的, 最简单的循环等待是双向无尽等待, 这是由于2个进程对多个公共资源的恶性竞争引起的
如果你对这一套解释云里雾里的话, 这儿有一个非常通俗的类比: 在一个参观中只剩下一双筷子, 2只筷子散落2处, 现在也只有2个顾客, 只要谁抢到了2只筷子就可以先吃饭, 对方就不能干扰, 因为法律规定不能干扰他人吃饭, 法律的规定看似很公平, 但是有一种特殊情况不容忽视: 当2个人分别抢到一只筷子后都指望对方放弃筷子, 让自己先吃, 对方后吃. 从时间效益的角度看, 无论谁先吃后吃, 总时间一定, 没有效率差别, 但是对于食客个人而言, 吃饭先后顺序和自己的利益息息相关, 于是谁都不愿意先放弃手中的筷子, 2个人僵在一起, 无尽的等待, 但是这样做又没有违法, 于是这个局面无解, 形成了死锁.
这个例子映射了经典的囚徒困境问题, 即个人利益最大化不等于集体最优.
囚徒困境(prisoner's dilemma)是指两个被捕的囚徒之间的一种特殊博弈,说明为什么甚至在合作对双方都有利时,保持合作也是困难的。囚徒困境是博弈论的非零和博弈中具代表性的例子,反映个人最佳选择并非团体最佳选择。虽然困境本身只属模型性质,但现实中的价格竞争、环境保护、人际关系等方面,也会频繁出现类似情况。
如图, 对于一个囚徒来说, 如果自己和对方都隐瞒对方, 则皆大欢喜, 双双释放, 如果都揭发对方, 双双坐牢1年, 如果自己隐瞒但被对方揭发则自己坐牢5年对方释放, 如果自己揭发对方对方隐瞒则自己释放对方坐牢5年. 如果他们是亲人那很有可能都坦承从而达到全局最优, 但是若双方都不信任, 对于个人来说最好的选择是揭发对方, 正中警察圈套!所以说, 在个体之间不信任的前提下, 囚徒困境是很难有解的.
仔细想想看, 真的无解吗? 其实有解, 如果我遇到这种情况, 按以前的性格, 直接上去抽对方也不会和他僵持(夸张了, 表达这个意思). 但是现在我不想和他浪费时间, 如果对方执意要先行, 我就把筷子给他, 等他吃完我再吃, 这样既节省了我的时间也节省了对方的时间, 更节省了饭店的时间. 但是现实中很少有人会这么做, 在没有控制全局的leader的前提下, 大多数人都会僵持.
进程之间难道不也是这样吗? 如果一个进程发现自己的很有可能处于死锁状态的时候自愿放弃手中的资源, 隔段时间再从新请求, 很有可能想要的资源顺利得到了, 这是真实可行的, 你们可以自己分析分析是不是这个道理, 一个进程只要暂时牺牲自己的时间, 从数学期望的角度就一定能获得之后的所有资源, 从而为自己节省更多的时间. 但是进程和人是一样的, 确实有少部分进程心胸宽广, 愿意放弃资源, 或者它觉得资源不充足的情况下可以照样办事, 办完后把手头的资源也释放, 让对方进程从而也得以继续, 但是大多数进程都是谁都不愿先妥协, 谁都不愿先吃亏, 相互抵制, 从而造成了囚徒困境, 产生了死锁.
既然囚徒困境在充满冷漠和质疑的世界上是无解的, 那就需要一个leader来控制局面, 这个leader就是OS. 但正如之前抢筷子问题, 即使两人的争吵引来了城管, 城管因为公平的原则也不能剥夺任何人的筷子给与对方, OS也不能强行把一方的资源给另一方先使用, 只能寻找其他的办法, 于是就产生了'银行家算法'.
回到之前的饭馆, 饭馆经理经过深思熟虑之后, 制定了一条行为准则, 专门针对饭店里存在的死锁问题, 这个规则不是为了死锁发生后怎样解决, 而是为了从根源上预防死锁, 法案规定, 筷子和其他餐具不再直接让顾客自由拿取, 而让顾客排队领取, 并且每个顾客进门之前要登记, 同时汇报自己对每一种餐具的数量需求, 如果是团体顾客就提供每人的需求总和.
为了简化例子, 还是拿刚刚2个人挣一只筷子的情况吧, 2个人A和B对筷子的需求量都是2, A申请领取第一支筷子的时候, 服务员会模拟一个假设: 假如把筷子给了A, 剩下1只筷子, 和一个需要2只筷子的B, 这样是否安全? 如果我把另外一只先给A, 等A满足后再把2只筷子给B, 正好, 没有危险. 于是服务员愉快的将第一支筷子给到A. 然后当B索要一只筷子的时候, 虽然库存中正好还有1只筷子, 但服务员还是拒绝了B的请求, 原因是, 如果筷子给了你, 你们有可能发生死锁.
这项规定的逻辑是, 你如果需要2只筷子, 却只得到了1只, 那你有权利不满, 有权利争吵; 但是如果2只筷子都给你了, 你给我闭嘴乖乖吃饭, 快点吃饭, 吃完把筷子还给我! 如果你进门时说只需要1只筷子, 现在要2只, 对不起, 给我滚! 这个方法是有代价的, 因为也许A并不是同时需要2只筷子, 有可能A用一只也能吃(也正打算用1只吃饭, 另外一只突然不想要了), 这种情况下也许把第二只筷子给到B是全局更好的方案, 但是服务员也不想冒险, 因为万一一会A又来索要第2只筷子了怎么办.
这就是银行家算法, 银行家算法是一种妥协的算法, 虽然避免了死锁但是有机会成本的开销. OS不可能假定每个进程都是高素质的, 只能考虑最坏的情况, 也就是每个进程不得到所有的资源不死心. 在每个进程请求资源的时候进行假设, 判断是否会造成不安全状态. 但如图所示, 不安全状态是包含死锁状态的, 也就是说系统不安全并不一定造成死锁, 银行家算法通过封杀所有不安全状态从而全面避免了死锁, 但资源浪费的情况也无法杜绝. 悠悠青史告诉我们, 在没有第三方权威约束下独立思维永远无法相互协作, 只能实现个体最优, 一个社会没有上帝引导, 通过自由发展永远无法共产.
总结
银行家算法的本质是OS的猜测算法, 是OS与进程以及进程与进程间的不信任引起的. 计算机软件本身就是由多个不信任的软件层结合起来的, 比如OS不信任浏览器, 浏览器不信任服务器, 服务器不信任用户, 在我们web领域, 这种4层不信任体系成为了了网页性能的瓶颈, 每一层不信任都带来了额外的时空开销. web的未来应该是高内聚低耦合的, 浏览器也应该从应用软件变成系统软件, 从而减少臃肿的层次结构, 提高互联网服务的性能, 目前为止这还是一场梦.