最近在复习<操作系统>的重修考试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个人僵在一起, 无尽的等待, 但是这样做又​没有违法,​ 于是这个局面无解, 形成了死锁.

这个例子映射了经典的​​囚徒困境​​问题, 即​个人利益最大化不等于集体最优.

死锁与囚徒困境的辩证关系_囚徒困境_02死锁与囚徒困境的辩证关系_服务器_03


囚徒困境(prisoner's dilemma)是指两个被捕的囚徒之间的一种特殊博弈,说明为什么甚至在合作对双方都有利时,保持合作也是困难的。囚徒困境是博弈论的非零和博弈中具代表性的例子,反映个人最佳选择并非团体最佳选择。虽然困境本身只属模型性质,但现实中的价格竞争、环境保护、人际关系等方面,也会频繁出现类似情况。


如图, 对于一个囚徒来说, 如果自己和对方都隐瞒对方, 则皆大欢喜, 双双释放, 如果都揭发对方, 双双坐牢1年, 如果自己隐瞒但被对方揭发则自己坐牢5年对方释放, 如果自己揭发对方对方隐瞒则自己释放对方坐牢5年. 如果他们是亲人那很有可能都坦承从而达到全局最优, 但是若双方都不信任, 对于个人来说最好的选择是揭发对方, 正中警察圈套!所以说, 在个体之间不信任的前提下, 囚徒困境是很难有解的.

仔细想想看, 真的无解吗? 其实有解, 如果我遇到这种情况, 按以前的性格, 直接上去抽对方也不会和他僵持(夸张了, 表达这个意思). 但是现在我不想和他浪费时间, 如果对方执意要先行, 我就把筷子给他, ​等他吃完我再吃,​ 这样​既节省了我的时间也节省了对方的时间, 更节省了饭店的时间. ​但是现实中很少有人会这么做, 在​没有控制全局的leader​的前提下, 大多数人都会僵持.

进程之间难道不也是这样吗? 如果一个进程​发现自己的很有可能处于死锁状态的时候自愿放弃手中的资源, 隔段时间再从新请求, 很有可能想要的资源顺利得到了​, 这是真实可行的, 你们可以自己分析分析是不是这个道理, 一个进程只要暂时牺牲自己的时间, 从​数学期望的角度​就​一定​能获得之后的所有资源, 从而为自己节省更多的时间. 但是进程和人是一样的, 确实有少部分进程​心胸宽广​, 愿意放弃资源, 或者它觉得资源不充足的情况下可以照样办事, 办完后把手头的资源也释放, 让对方进程从而也得以继续, 但是大多数进程都是谁都不愿先妥协, 谁都不愿先吃亏, 相互抵制, 从而造成了囚徒困境, 产生了死锁.

既然囚徒困境在充满冷漠和质疑的世界上是无解的, 那就需要一个leader来控制局面, 这个leader就是OS. 但正如之前抢筷子问题, 即使两人的争吵引来了城管, 城管因为​公平的原则​也不能剥夺任何人的筷子给与对方, OS也不能强行把一方的资源给另一方先使用, 只能寻找其他的办法, 于是就产生了'​​银行家算法​​'.

死锁与囚徒困境的辩证关系_服务器_04

回到之前的饭馆, 饭馆经理经过深思熟虑之后, 制定了一条行为准则, 专门针对饭店里存在的死锁问题, 这个规则不是为了死锁发生后怎样解决, 而是为了​从根源上预防死锁, ​法案规定, 筷子和其他餐具不再直接让顾客自由拿取, 而让顾客​排队领取​, 并且每个顾客进门之前要登记, 同时汇报自己对每一种餐具的数量需求, 如果是团体顾客就提供每人的需求总和.

为了简化例子, 还是拿刚刚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的未来应该是高内聚低耦合的, 浏览器也应该从应用软件变成系统软件, 从而减少臃肿的层次结构, 提高互联网服务的性能, 目前为止这还是一场梦.