linux內核的併發與競態的理解

一、概念理解

1.概述
對於我們的驅動程序,或一個代碼片段,是可以被兩個獨立的進程同時執行。驅動程序,或者代碼片段對cpu來說就是機器指令,顯然指令是可以同時發出的。但是共享資源卻不能同時使用,不然就沒法統計了。

2.競態
多個進程同時執行某一代碼段,或者調用同一個驅動程序;而這一代碼段涉及到對內存的分配,或者驅動程序對硬件進行訪問;由於內存或者硬件資源對系統來說都是共享的,那麼就容易出問題。比如A進程剛剛分配的內存還沒有來得及保存,B進程又重新執行相同的代碼段分配了相同的內存,進行保存,這樣A進程分配的內存就丟失了。對於這樣一種同時訪問共享資源出現的競爭態勢,稱之爲競態。

3.併發
正是因爲併發的狀態,才導致竟態。所以,像上述描述的多個進程同時執行同一代碼片段或調用同一驅動程序的狀態,稱之爲併發狀態,簡稱併發。當然併發來源不止這些,比如:內核是可搶佔的,當前驅動程序很容易丟失對cpu的獨佔,而擁有cpu的進程可能正在調用我們的驅動程序;設備中斷是異步事件,也會導致代碼併發執行。還有延遲機制如工作隊列,tasklet,定時器會在未來任何時刻執行,而不管當前進程在幹什麼。再有熱插拔,在使用過程中設備就消失了。

4.竟態通常作爲對資源的共享訪問結果而產生,而併發簡單理解,就是進程同時執行某一代碼段,而不用管其他進程是否正在訪問這一代碼段的狀態。

二、應對策略

我們需要重新設計,使得代碼要麼看到已經分配好的內存,要麼知道內存還沒有分配或將要由其他人分配。訪問管理的常見技術稱爲“鎖定”或者“互斥”。

1.原子性操作

  • 指這一操作過程是獨佔cpu的,不能被打斷的,所以其結果是cpu要麼完成,要麼失敗,而不存在被中止,打斷,掛起的情況。

2.信號量與互斥體

  • a.信號量就是對信號量值進行PV操作,希望進入臨界區的進程需要進行P操作減1,進程繼續,如果信號量值爲0或者小於0,則進程必須等待知道其他人釋放該信號量。對信號量的解鎖通過調用V操作,增加信號量的值,並在必要的時候喚醒等待該信號量的進程,因爲獲取不到該信號量值的進程大部分情況都是進入睡眠狀態。
  • b.如果共享資源很多,則允許多個進程同時執行,直到信號量變爲0或小於0,進程才需要等待。如果共享資源只有1個,即同時只允許一個進程執行,即信號量值初始化爲1,這種情況下,一個信號量就是互斥體,簡稱互斥。而linux內核中幾乎所有的信號量均用於互斥。但是linux內核中真正的互斥鎖mutex也是存在的,只是功能上與信號值爲1的狀態完全一致。
  • c.但是互斥體與信號量還是有本質的區別:互斥體是大家排隊,一個一個來。而信號則可以控制訪問順序,比如生產者,消費者。互斥體加鎖是誰先加鎖,誰誰釋放。而信號量則是成對的,不同的線程之間也可以進行PV操作,A進行V操作,B就可以進行P操作,這個過程其實就是同步,控制順序。當然也可以是互斥,比如,A先進行P操作,然後進行V操作,這種就是與互斥體mutex一模一樣了。
  • d.信號量與互斥體在獲取不到的時候,都會進入睡眠狀態,進程被掛起,讓出cpu。同時這兩個互斥機制都是針對進程或線程而言,且被掛起,被喚醒是要進行進程上下文切換,消耗的資源比較大,一般適合臨界區較大,且允許睡眠。
  • e.信號量獲取的三種狀態(P操作):
    down()
    獲取不到信號量則睡眠,在必要時一直等待
    down_interruptible()
    獲取不到信號量則睡眠,但是可被用戶中斷,如果不能被殺死,用戶會懊惱。但 是操作被中斷,函數返回非零值,且用戶不會擁有信號量,故需檢查返回值,判斷是哪種情況的退出
    down_trylock()
    獲取不到信號量立刻返回一個非零值,不會導致進程睡眠。
    up()
    釋放信號量,但要特別注意在出現錯誤的情況下,記得在返回之前調用up()釋放該信號量
  • f.互斥體mutex獲取的三種狀態:
    mutex_lock()
    mutex_lock_interruptible()
    mutex_lock_try()
    mutex_is_locked()
    mutex_unlock() 此3個函數與上述解釋一致
  • g.linux內核中一般會定義一個大的結構體表示一個設備,比如一個cdev字符設備,一般會定義一個更大的結構體包含cdev,也包含對cdev的附加資源。而對於這種硬件設備資源,需要互斥鎖的機制進行保護。當使用信號量時,一般放在該結構體內,原因是:對每個cdev字符設備使用單獨的信號量,使用單個的全局信號量也是正確的,但是不同的cdev字符設備並不共享資源
    ,因此沒有理由讓一個進程在其他進程訪問不同的cdev字符設備時等待。
    3.完成量
  • 完成量與信號量成對使用是類似的,一個進程進行V操作,另一個進程才能進行P操作,如果沒有進行V操作,則V操作的進程將等待。對於完成量,一個進程進行complete/comple_all操作,另一個進程wait_for_completion才能繼續。如果沒有進行complete/comple_al操作,另一個wait_for_completion進程將等待。而同一個進程單獨使用PV操作,且信號量的值爲1時,這種情況與互斥體mutex沒有本質的區別。另外,complete更多的使用在線程裏面,屬於輕量級的。complete,semaphore,mutex三者在獲取不到互斥鎖時均會睡眠等待。
    4.自旋鎖
  • a.自旋鎖在獲取不到的時候,本身是不會睡眠的,也就是說,如果某個進程獲取自旋鎖後,將是獨佔cpu,以爲自旋鎖本身會關閉搶佔,除非自旋鎖鎖定的區間,代碼自己將自己切換出去。
  • b.自旋鎖本身在獲取的過程中,實際上上測試並設置某個整數值的某個位,那麼這個操作也必須是原子性的,這樣就可以確保多個線程在給定的時間裏自旋,也只有一個線程可以獲得該鎖。假如測試並設置某個整數值的某個爲不是原子性操作,比如a線程測試後ok,準備設置某個位(也即上鎖),這時a線程被中斷了(比如更高優先級的線程獲得cpu),b線程繼續測試後,發現也是ok的,因爲a線程還沒來得及設置,緊接着b線程開始設置,也即獲得自旋鎖。當a線程在某個時間重新獲得cpu後,繼續設置,也獲得自旋鎖,那麼此時將有兩個線程同時獲得自旋鎖,更壞的情況可能是更多線程獲取自旋鎖,所以如果自旋鎖本身的測試並設置操作不是原子性的,那麼自旋鎖本身也就失去了互斥的意義。可見自旋鎖還需要依賴原子變量操作,粒度更細的原子性操作。
  • c.自旋鎖的三條準則:
    1.
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章