【干货】同步与互斥的失败例子
韋東山老師最新錄制的驅(qū)動(dòng)大全之<<同步與互斥>>收費(fèi)視頻已經(jīng)在淘寶上架銷售 ,一共7節(jié),良心價(jià)29元,同時(shí)已經(jīng)同步到CSDN , 51CTO , 電子發(fā)燒友,騰訊課堂等平臺(tái)。
本文是其中一節(jié)《同步與互斥的失敗例子》視頻配套文檔,
《同步與互斥的失敗例子》免費(fèi)試看版(2分鐘,完整版7分46秒):
《同步與互斥的失敗例子》完整版 購買鏈接:
https://item.taobao.com/item.htm?id=620987021249? (復(fù)制到瀏覽器打開)
光看文檔可能難度稍大,建議結(jié)合視頻進(jìn)行學(xué)習(xí);gg完畢,開始干貨。
1.2 同步與互斥的失敗例子
注意:本節(jié)在GIT上沒有源碼。
GIT地址:
git clone https://e.coding.net/weidongshan/01_all_series_quickstart.git
一句話理解同步與互斥:我等你用完廁所,我再用廁所。什么叫同步?就是條件不允許,我要等等。什么是互斥?你我早起都要用廁所,誰先搶到誰先用,中途不被打擾。
同步與互斥經(jīng)常放在一起講,是因?yàn)樗鼈冎g的關(guān)系很大,“互斥”操作可以使用“同步”來實(shí)現(xiàn)。我“等”你用完廁所,我再用廁所。這不就是用“同步”來實(shí)現(xiàn)“互斥”嗎?有時(shí)候看代碼更容易理解,偽代碼如下:
01?void??搶廁所(void) 02?{ 03????if?(有人在用)?我瞇一會(huì); 04????用廁所; 05????喂,醒醒,有人要用廁所嗎; 06?}假設(shè)有A、B兩人早起搶廁所,A先行一步占用了;B慢了一步,于是就瞇一會(huì);當(dāng)A用完后叫醒B,B也就愉快地上廁所了。
在這個(gè)過程中,A、B是互斥地訪問“廁所”,“廁所”被稱之為臨界資源。我們使用了“休眠-喚醒”的同步機(jī)制實(shí)現(xiàn)了“臨界資源”的“互斥訪問”。
上面是一個(gè)有“味道”的例子,回到程序員的世界,一個(gè)驅(qū)動(dòng)程序同時(shí)只能有一個(gè)APP使用,怎么實(shí)現(xiàn)?
1.2.1 失敗例子1
01?static?int?valid?=?1; 02 03?static?ssize_t?gpio_key_drv_open?(struct?inode?*node,?struct?file?*file) 04?{ 05??????if?(!valid) 06??????{ 07??????????????return?-EBUSY; 08??????} 09??????else 10??????{ 11??????????????valid?=?0; 12??????} 13 14??????return?0;?//成功 15?} 16 17?static?int?gpio_key_drv_close?(struct?inode?*node,?struct?file?*file) 18?{ 19??????valid?=?1; 20??????return?0; 21?} 22看第5行,我們使用一個(gè)全局變量valid來實(shí)現(xiàn)互斥訪問。這有問題嗎?很大概率沒問題,但是并非萬無一失。
注意:編寫驅(qū)動(dòng)程序時(shí),要有系統(tǒng)的概念,程序A調(diào)用驅(qū)動(dòng)程序時(shí),它可能被程序B打斷,程序B也去調(diào)用這個(gè)驅(qū)動(dòng)程序。下圖是一個(gè)例子,程序A在調(diào)用驅(qū)動(dòng)程序的中途被程序B搶占了CPU資源:
程序A執(zhí)行到第11行之前,被程序B搶占了,這時(shí)valid尚未被改成0;程序B調(diào)用gpio_key_drv_open時(shí),發(fā)現(xiàn)valid等于1,所以成功返回0;當(dāng)程序A繼續(xù)從第11行執(zhí)行時(shí),它最終也成功返回0;這樣程序A、B都成功打開了驅(qū)動(dòng)程序。
注意:在內(nèi)核態(tài),程序A不是主動(dòng)去休眠、主動(dòng)放棄CPU資源;而是被優(yōu)先級(jí)更高的程序B搶占了,這種行為被稱為“preempt”(搶占)。
1.2.2 失敗例子2
上面的例子是不是第5行到第11行的時(shí)間跨度大長了?再優(yōu)化一下程序行不行?代碼如下:
01?static?int?valid?=?1; 02 03?static?ssize_t?gpio_key_drv_open?(struct?inode?*node,?struct?file?*file) 04?{ 05??????if?(--valid) 06??????{ 07??????????????valid++; 08??????????????return?-EBUSY; 09??????} 10??????return?0; 11?} 12 13?static?int?gpio_key_drv_close?(struct?inode?*node,?struct?file?*file) 14?{ 15??????valid?=?1; 16??????return?0; 17?} 18第5行先減1再判斷,這樣可以更大概率地避免問題,但是還是不能確保萬無一失。對(duì)數(shù)據(jù)的修改分為3步:讀出來、修改、寫進(jìn)去。請(qǐng)看下圖:
進(jìn)程A在讀出valid時(shí)發(fā)現(xiàn)它是1,減1后為0,這時(shí)if不成立;但是修改后的值尚未寫回內(nèi)存;假設(shè)這時(shí)被程序B搶占,程序B讀出valid仍為1,減1后為0,這時(shí)if不成立,最后成功返回;輪到A繼續(xù)執(zhí)行,它把0值寫到valid變量,最后也成功返回。這樣程序A、B都成功打開了驅(qū)動(dòng)程序。
1.2.3 失敗例子3
前面2個(gè)例子,都是在修改valid的過程中被別的進(jìn)程搶占了,那么在修改valid的時(shí)候直接關(guān)中斷不就可以了嗎?
01?static?int?valid?=?1; 02 03?static?ssize_t?gpio_key_drv_open?(struct?inode?*node,?struct?file?*file) 04?{ 05???????unsigned?long?flags; 06???????raw_local_irq_save(flags);?//?關(guān)中斷 07??????if?(--valid) 08??????{ 09??????????????valid++; 10??????????????raw_local_irq_restore(flags);??//?恢復(fù)之前的狀態(tài) 11??????????????return?-EBUSY; 12??????} 13???????raw_local_irq_restore(flags);??????????//?恢復(fù)之前的狀態(tài) 14??????return?0; 15?} 16 17?static?int?gpio_key_drv_close?(struct?inode?*node,?struct?file?*file) 18?{ 19??????valid?=?1; 20??????return?0; 21?}第06行直接關(guān)中斷,這樣別的線程、中斷都不能來打擾本線程了,在它讀取、修改valid變量的過程中無人打擾。沒有問題了?
對(duì)于單CPU核的系統(tǒng)上述代碼是沒問題的;但是對(duì)于SMP系統(tǒng),你只能關(guān)閉當(dāng)前CPU核的中斷,別的CPU核還可以運(yùn)行程序,它們也可以來執(zhí)行這個(gè)函數(shù),同樣導(dǎo)致問題,如下圖:
假設(shè)CPU 0上進(jìn)程A、CPU1上進(jìn)程B同時(shí)運(yùn)行到上圖中讀出valid的地方,它們同時(shí)發(fā)現(xiàn)valid都是1,減減后都等于0,在第07行判斷條件都不成立,所以在第14行都可以返回0,都可以成功打開驅(qū)動(dòng)。
推薦閱讀:
? ??專輯|Linux文章匯總
? ??專輯|程序人生
? ??專輯|C語言
嵌入式Linux
微信掃描二維碼,關(guān)注我的公眾號(hào)?
總結(jié)
以上是生活随笔為你收集整理的【干货】同步与互斥的失败例子的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python进阶之学习笔记_Python
- 下一篇: SARIMA时间序列模型预测城市房价数据