本年度第 10 次操作系統(tǒng)成員會議開始啦!
一月一度的會議旨在讓大家互相交流,解決最近在工作中出現(xiàn)的問題佃蚜,以提高整個計算機(jī)系統(tǒng)的工作效率。因?yàn)橛嬎銠C(jī)硬件在飛速發(fā)展着绊,而操作系統(tǒng)是連接計算機(jī)硬件和應(yīng)用程序的中間層谐算,如果故步自封,很快就會被市場淘汰归露,所以每位操作系統(tǒng)成員都很重視月度會議洲脂。
這次提出問題的是進(jìn)程和線程兩兄弟。
站在眾人前面剧包,線程顯得有些怯場恐锦,他戳了戳進(jìn)程往果,示意讓他先來講。進(jìn)程迅速整理了下思路一铅,挺直了身板陕贮,說:“這次的問題是在一個訂票系統(tǒng)里發(fā)現(xiàn)的,我把這個系統(tǒng)的簡單邏輯畫出來了潘飘,你們一邊看我一邊說肮之。”
“這個訂票系統(tǒng)分為服務(wù)器端(server)和客戶端(client)卜录,當(dāng)用戶與服務(wù)器建立連接時戈擒,服務(wù)器端就會建立一個新的線程來為客戶端提供服務(wù)。訂票邏輯是這樣的:
單獨(dú)從這個邏輯圖上看是沒有問題的暴凑,但在實(shí)際情況下峦甩,因?yàn)榻?jīng)常出現(xiàn)多個用戶同時搶訂一張票的情景,這種方式就可能會出錯现喳。就像這樣:
在線程 A 確定完余票(假設(shè)是 1)凯傲,但還未能成功訂票之前,線程 B 得到了余票數(shù)為 1 的信息嗦篱,所以 B 也認(rèn)為可以訂票冰单,最后導(dǎo)致一張票賣出去兩份【拇伲“
內(nèi)存一針見血的道:“我看這就是幾個線程執(zhí)行流的沖突問題嘛诫欠,本來應(yīng)該一個線程訂票操作結(jié)束后,另一個線程才能查詢余票浴栽。像這樣執(zhí)行流交叉荒叼,肯定還會出現(xiàn)其它意想不到的問題〉浼Γ”
進(jìn)程佩服的說:“誒別說被廓,內(nèi)存你說的太有道理了,我也遇到過類似的情況萝玷,上次我和另一個進(jìn)程共享一部分內(nèi)存空間嫁乘,結(jié)果在使用同一個數(shù)據(jù)的時候,他把我剛寫進(jìn)去的數(shù)據(jù)覆蓋掉了球碉,害得我后面的計算全出錯了蜓斧。”
這時睁冬,磁盤發(fā)表了他的看法:“執(zhí)行流的問題挎春,那一看就是進(jìn)程調(diào)度器的鍋,怎么非得在別人執(zhí)行到關(guān)鍵步驟的時候把人家從 CPU 上趕下來!要是調(diào)度器稍微等一會兒搂蜓,這問題不就解決了狼荞?”
進(jìn)程調(diào)度器聽到這話,氣的站起來帮碰,說:“你相味,你怎么憑空污人清白!什么時候切換進(jìn)程不是由我來決定好不好殉挽?我是負(fù)責(zé)從就緒隊(duì)列里選出最應(yīng)該使用 CPU 的進(jìn)程而已丰涉。等我開始調(diào)度的時候,那些進(jìn)程就已經(jīng)被操作系統(tǒng)撤下來了斯碌∫凰溃”
操作系統(tǒng)補(bǔ)充道:“調(diào)度器說的沒錯,調(diào)度的時機(jī)是由中斷決定的傻唾⊥洞龋看樣子這種情況出現(xiàn)在進(jìn)程時間片用盡的時候,出現(xiàn)了時鐘中斷冠骄,然后被其他進(jìn)程搶占了 CPU 資源伪煤。”
磁盤聽了凛辣,不好意思的說:“對不起抱既,剛剛是我太武斷了。那照你的意思扁誓,我們在執(zhí)行到這部分代碼的時候防泵,像這樣屏蔽時鐘中斷可以解決這個問題了?”
操作系統(tǒng)搖搖頭:“「中斷禁用」這種方式確實(shí)可以防止進(jìn)程在運(yùn)行這部分代碼時進(jìn)行切換蝗敢,但是捷泞,時鐘中斷是我的一項(xiàng)非常重要的功能,怎么能隨隨便便就把控制權(quán)交給人類呢寿谴?萬一有的程序員想要他們的代碼可以完全占有 CPU 锁右,不把時鐘中斷給我開啟怎么辦?我是不可能把這種重要權(quán)限交出去的拭卿,我要對整個系統(tǒng)負(fù)責(zé)骡湖〖溃”
內(nèi)存在旁邊贊同道:“除了這一方面峻厚,你還要知道,現(xiàn)在都是多核時代了谆焊,你即使禁用了這個 CPU 的時鐘中斷惠桃,其他幾個核還是能切換進(jìn)程,然后訪問這些數(shù)據(jù)。磁盤啊辜王,你明明存了那么多文件劈狐,怎么懂得還是那么少。呐馆。肥缔。”
磁盤憤憤的道:“別瞧不起我汹来,我這就去找有沒有辦法解決這個問題续膳!”
思考了許久的 CPU 開口了:“我來捋一捋吧,現(xiàn)在咱的目標(biāo)是收班,不讓兩個進(jìn)程同時執(zhí)行這一段代碼——我們把這段代碼叫做臨界區(qū)吧坟岔,換句話說,我們需要讓進(jìn)程互斥的進(jìn)入臨界區(qū)摔桦。那我們就把這段臨界區(qū)「加鎖」社付,”
“加鎖?這是什么意思邻耕?”
“加鎖是個比喻鸥咖,其實(shí)「鎖」只是一個共享變量,我們可以讓它有 OPEN
和 CLOSE
這兩個值赊豌。一個進(jìn)程扛或,比如說 A,進(jìn)入臨界區(qū)之前碘饼,先檢查鎖是不是 OPEN
狀態(tài)熙兔,如果是的話,就把鎖改為 CLOSE
狀態(tài) 艾恼,這樣其他進(jìn)程在進(jìn)入臨界區(qū)時住涉,會發(fā)現(xiàn)鎖已經(jīng) CLOSE
了,那就讓他們循環(huán)等待 钠绍,直到 A 出臨界區(qū)然后將鎖打開舆声。”
內(nèi)存眉頭一皺柳爽,發(fā)現(xiàn)事情并沒有這么簡單——如果 A 發(fā)現(xiàn)鎖是開著的媳握,但在 A 還沒有關(guān)閉鎖之前,切換到了進(jìn)程 B 磷脯,那么 B 也會發(fā)現(xiàn)鎖是開著的蛾找,那么 B 也將能夠進(jìn)入臨界區(qū)!
想到這里赵誓,內(nèi)存把問題告訴 CPU打毛,但 CPU 說柿赊,這對他不是問題。
原來計算機(jī)里有一條硬件支持的指令——TSL(test and set lock幻枉,測試并加鎖)碰声,這條指令可以保證讀字和寫字的操作「不可分割」,也就是說熬甫,在這條指令結(jié)束前胰挑,就連其他處理器也不可能訪問該內(nèi)存字。
“TSL 指令會把內(nèi)存字 lock 讀到寄存器上椿肩,然后在對應(yīng)的內(nèi)存地址上寫入一個非零值洽腺。那我們就可以利用這條指令改進(jìn)剛剛的加鎖的方法,就像這樣:
我們讓進(jìn)程在進(jìn)入臨界區(qū)之前先調(diào)用 enter_region
覆旱,如果鎖已經(jīng)被關(guān)閉(表現(xiàn)為鎖非 0 )蘸朋,就循環(huán)調(diào)用enter_region
,直到鎖打開扣唱,然后再進(jìn)入臨界區(qū)藕坯。出臨界區(qū)之后,就調(diào)用 leave_region
把鎖打開噪沙。這樣不就解決你的問題了炼彪?“
內(nèi)存點(diǎn)點(diǎn)頭,說:“這確實(shí)是一個好方法正歼,解決了臨界區(qū)的互斥問題辐马。”
不過操作系統(tǒng)不是很滿意這種解決方案:“這種解決方式需要忙等待局义,浪費(fèi)了 CPU 的資源啊喜爷,我覺得這種 TSL 方案需要改進(jìn)√汛剑”
這時候大家陷入了沉默——誰也沒有想到更好的解決方案檩帐,會議好像就此僵住了。
誰能想到一種更好的方案呢另萤?
哈哈湃密,我在文章里埋了伏筆哦,你猜猜是誰找到了更好的方法呢四敞?
覺得我寫的還不錯的話泛源,就點(diǎn)個贊吧!
聲明:原創(chuàng)文章忿危,未經(jīng)授權(quán)达箍,禁止轉(zhuǎn)載