1. 程式人生 > >死鎖與囚徒困境的辯證關係

死鎖與囚徒困境的辯證關係

最近在複習<作業系統>的重修考試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的未來應該是高內聚低耦合的, 瀏覽器也應該從應用軟體變成系統軟體, 從而減少臃腫的層次結構, 提高網際網路服務的效能, 目前為止這還是一場夢.