天天看點

核心同步介紹

版權聲明:您好,轉載請留下本人部落格的位址,謝謝 https://blog.csdn.net/hongbochen1223/article/details/48318965

(一):臨界區和競争條件

所謂臨界區(也稱為臨界段)就是通路和操作共享資料的代碼段.多個執行線程并發通路同一個資源通常是不安全的,為了避免在臨界區中并發通路,程式設計者必須保證這些代碼原子的執行--也就是說,操作在執行結束前不能被打斷,就如同整個臨界區是一個不可分割的指令一樣.如果兩個執行線程有可能處于同一個臨界區中同時執行,那麼這就是程式包含的一個bug.如果這種情況确實發生了,我們就成他是競争條件,這樣命名是因為這裡會存線上程競争.避免并發和防止競争條件稱為同步.

1:單個變量

現在,首先看一個特殊計算的例子.考慮一個非常簡單的共享資源:一個全局整型變量和一個簡單的臨界區.其中的操作僅僅是将整型變量的值增加1.

i++;

該操作可以轉換成類似于下面動作的機器指令序列:

得到目前變量i 的值并且拷貝到一個寄存器中

将寄存器中的值加1

把i的新值寫會到記憶體中

現在假定有兩個執行線程同時進入臨界區,如果i的初始值是7,那麼我們所期望的結果應該像下面這樣(每一行代表一個時間單元):

線程1        線程2
擷取i(7)         --------
增加i(7->8)    --------
寫回i(8)         --------
---------          獲得i(8)
---------          增加i(8->9)
---------          寫回i(9)
           

這是我們所期望的,但是實際上的運作情況可能是這樣的:

線程1        線程2
獲得i(7)        獲得i(7)
增加i(7->8)   ---------
-------------     增加i(7->8)
寫回i(8)        -----------
------------     寫回i(8)
           

這樣就不是我們希望出現的結果了,但是實際情況中這種現象也是可能會發生的.那麼這種情況解決起來也是相對比較簡單的---我們僅僅需要将這些指令作為一個不可分割的整體來執行就可以了.多數處理器都提供了指令來原子的讀變量,增加變量,然後寫回變量,使用這樣的指令就能解決一些問題.使用這個指令唯一的可能結果為:

線程1        線程2
增加i(7->8)    ---------
--------------    增加i(8->9)
           

或者是相反

線程1        線程2
--------    增加i(7->8)
增加i(8->9)    ---------
           

兩個原子操作執行根本就不可能發生,因為處理器會從實體上確定這種不可能.

(二):加鎖

鎖有多種多樣的形式,而且加鎖的粒度也各不相同---Linux自身實作了幾種不同的鎖機制.各種鎖機制之間的差別主要在于:當鎖已經被其他線程持有,因而不可用時的行為表現--一些鎖被争用時會簡單的執行忙等待,而另外一些鎖會使目前任務睡眠知道鎖可用為止.

1:造成并發執行的原因

使用者空間之是以需要同步,是因為使用者程式會被排程程式搶占和重新排程.由于使用者程序可能在任何時刻被搶占,而排程程式完全可能選擇另一個高優先級的程序到處理器上執行,是以就會使得一個程式正處于臨界區的時候,被非自願地搶占了.如果新排程的程序随後也進入同一個臨界區(比如說這兩個程序要操作共享的記憶體,或者向同一個檔案描述符寫入),前後兩個程序互相之間就會産生競争.另外,因為信号處理是異步發生的,是以,即使是單線程的多個程序共享檔案,或者是在同一程式内部處理信号,也有可能産生競争條件.這種類型的并發操作--這裡其實兩者并不真是同時發生的,但他們互相交叉進行,是以也被稱作僞并發執行.

如果有一台支援對稱多處理器的機器,那麼兩個程序就可以真正的在臨界區中同時執行,這種類型稱為真并發.

核心中有類似可能造成并發執行的原因.他們是:

中斷--中斷幾乎可以在任何時刻異步發生,也就可能随時打斷目前正在執行的代碼

軟中斷和tasklet--核心能在任何時刻喚醒或排程軟中斷和tasklet,打斷目前正在執行的代碼

核心搶占--因為核心具有搶占性,是以核心中的任務可能會被另一個任務搶占

睡眠及使用者空間的同步--在核心執行的程序可能會睡眠,這就會喚醒排程程式,進而導緻排程一個新的使用者程序執行

對稱多處理器--兩個或多個處理器可以同時執行代碼

在中斷處理程式中能避免并發通路的安全代碼稱作中斷安全代碼;在對稱多處理器的機器中能避免并發通路的安全代碼稱為SMP安全代碼(SMP-safe).在核心搶占的時候能避免并發通路的安全代碼稱為搶占安全代碼.

2:需要給那些資料加鎖

如果有其他線程可以通路這些資料,那麼就給這些資料加上鎖;如果任何其他什麼東西都能看到他,那麼就要鎖住他.記住:要給資料加鎖而不是給代碼加鎖.

(三)死鎖

死鎖的産生需要一定條件:要有一個或多個執行線程和一個或多個資源,每個線程都在等待其中的一個資源,但所有的資源都已經被占用了.所有的線程都在互相等待,但他們永遠不會釋放已經占有的資源.于是任何線程都無法繼續,這便意味着死鎖的發生.

最簡單的死鎖的例子是自死鎖:如果一個執行線程試圖去獲得一個自己已經持有的鎖,他将不得不等待鎖被釋放,但他正在忙着等待這個鎖,是以自己永遠不會有機會釋放所,最終結果是死鎖.

一些簡單的規則對避免死鎖有非常大的幫助:

1:按順序加鎖,盡量按相反的順序釋放鎖.使用嵌套的鎖時,必須保證以相同的順序擷取鎖,這樣可以阻止緻命擁抱類型的死鎖.

2:防止發生饑餓

3:不要重複請求同一個鎖

4:設計力求簡單--越複雜的加鎖方案有可能造成死鎖

(四):争用和擴充性

鎖的争用,或簡稱争用,是指當鎖正在被占用的時候,有其他線程試圖獲得該鎖.說一個鎖處于高度争用狀态,就是指有多個其他線程在等待獲得該鎖.

擴充性是對系統可擴充程度的一個度量.

繼續閱讀