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