linux的併發與競爭

2021-10-07 20:09:09 字數 3582 閱讀 4219

自旋鎖訊號量

互斥體併發就是多個「使用者」同時訪問乙個資源(所以解決的方法就是讓這乙個資源每個時刻只能有乙個使用者訪問,hhh)

linux系統併發產生的原因,主要有以下幾種

上面指的資源就是臨界區臨界區就是共享資料段,對於臨界區必須保證一次只有乙個執行緒訪問

原子操作就是不能再進一步分割的操作,一般原子操作用於變數或者位操作,或許你會以為a=3;這個賦值操作是原子操作,但是c語言要先編譯為彙編指令,arm架構不支援直接對暫存器進行讀寫操作,比如要借助暫存器 r0、 r1 等來完成賦值操作。假設變數 a 的位址為 0x3000000,「a=3」這一行 c語言可能會被編譯為如下所示的彙編**:

ldr r0,

=0x30000000

/* 變數 a 位址 */

ldr r1,=3

/* 要寫入的值 */

str r1,

[r0]

/* 將 3 寫入到 a 變數中 */

linux核心定義了叫做atomic_t的結構體來完成整型資料的原子操作,在使用中用原子變數來代替整型變數,此結構體定義在include/linux/types.h檔案中,定義如下:

typedef

struct

atomic_t;

如果要使用原子操作api函式,首先要定義乙個atomic_t的變數,如下所示:

atomic_t a; //定義a

定義並賦值的如下:

atomic_t b = atomic_init(0); //定義原子變數b並賦初值為0

可以通過巨集 atomic_init 向原子變數賦初值。

上面是針對32位原子操作,64位原子操作函式只是將atomic_字首變成atomic64_

原子變數和api函式使用:

atomic_t v =

atomic_init(0

);//定義並初始化原子變數v=0

atomic_set(10

);//設定v=10

atomic_read

(&v)

;//讀取v的值,肯定是10

atomic_inc

(&v)

;//v的值加1,v=11

原子位操作是直接對記憶體進行操作,相關的api函式如表所示

自旋鎖是一種用於保護多執行緒共享資源的鎖,與一般互斥鎖不同之處在於自旋鎖嘗試獲取鎖時以忙等待的形式不斷的迴圈檢查鎖是否可用。

在多cpu的環境下,對持有鎖較短的程式來說,使用自旋鎖代替一般的互斥鎖往往能夠提高程式的效能

linux核心使用結構體spinlock_t表示自旋鎖:

64

typedef

struct spinlock

;74 #endif

75};76

}spinlock_t;

在使用自旋鎖之前,肯定要先定義乙個自旋鎖變數,定義方法如下:

spinlock_t lock;//定義自旋鎖

上述自旋鎖的api函式適用於smp(多核)或支援搶占的單cpu下執行緒之間的併發訪問,也就是用於執行緒與執行緒之間,被自旋鎖保護的臨界區一定不能呼叫任何能夠引起睡眠和阻塞的api函式,否則會導致死鎖現象的發生。

表中的 api 函式用於執行緒之間的併發訪問,如果此時中斷也要插一腳,中斷也想訪問共享資源,那該怎麼辦呢?首先可以肯定的是,中斷裡面可以使用自旋鎖,但是在中斷裡面使用自旋鎖的時候,在獲取鎖之前一定要先禁止本地中斷(也就是本 cpu 中斷,對於多核 soc來說會有多個 cpu 核),否則可能導致死鎖現象的發生

執行緒a先執行,並且獲得了lock這個鎖,當執行緒a執行到函式functiona的時候,發生了中斷,中斷服務函式也要獲得lock這個鎖,但是此時執行緒a一直占有lock這個鎖,中斷一直自旋,從而產生了死鎖。

最好的解決辦法是在獲取鎖之前關閉本地中斷

define_spinlock

(lock)

//定義並初始化乙個鎖

/*執行緒a*/

void

functiona()

/*中斷服務函式*/

void

irq(

)

相比於自旋鎖,訊號量可以使執行緒進入休眠狀態,比如 a 與 b、 c 合租了一套房子,這個房子只有乙個廁所,一次只能乙個人使用。某一天早上 a 去上廁所了,過了一會 b 也想用廁所,因為 a 在廁所裡面,所以 b 只能等到 a 用來了才能進去。 b 要麼就一直在廁所門口等著,等 a 出來,這個時候就相當於自旋鎖。 b 也可以告訴 a,讓 a 出來以後通知他一下,然後 b 繼續回房間睡覺,這個時候相當於訊號量。可以看出,使用訊號量會提高處理器的使用效率,畢竟不用一直傻乎乎的在那裡「自旋」等待。但是,訊號量的開銷要比自旋鎖大,因為訊號量使執行緒進入休眠狀態以後會切換執行緒,切換執行緒就會有開銷。總結一下訊號量的特點:

①、因為訊號量可以使等待資源執行緒進入休眠狀態,因此適用於那些占用資源比較久的場合。

②、因此訊號量不能用於中斷中,因為訊號量會引起休眠,中斷不能休眠。

③、如果共享資源的持有時間比較短,那就不適合使用訊號量了,因為頻繁的休眠、切換執行緒引起的開銷要遠大於訊號量帶來的那點優勢

linux 核心使用 semaphore 結構體表示訊號量,結構體內容如下所示:

struct semaphore 

;

有關訊號量的 api 函式如表

linux 提供了乙個比訊號量更專業的機制來進行互斥,它就是互斥體—mutex。互斥訪問表示一次只有乙個執行緒可以訪問共享資源,不能遞迴申請互斥體。在我們編寫 linux 驅動的時候遇到需要互斥訪問的地方建議使用 mutex。 linux 核心使用 mutex 結構體表示互斥體

struct mutex 

;

在使用 mutex 之前要先定義乙個 mutex 變數。在使用 mutex 的時候要注意如下幾點:

①、 mutex 可以導致休眠,因此不能在中斷中使用 mutex,中斷中只能使用自旋鎖。

②、和訊號量一樣, mutex 保護的臨界區可以呼叫引起阻塞的 api 函式。

③、因為一次只有乙個執行緒可以持有 mutex,因此,必須由 mutex 的持有者釋放 mutex。並且 mutex 不能遞迴上鎖和解鎖。

有關互斥體的 api 函式如表

Linux 併發與競爭詳解

多個任務 中斷都能訪問的資源叫共享資源,在驅動開發中注意對共享資源的保護,防止共享資源的併發訪問造成混亂。1 什麼是併發?答 併發就是多個 使用者 同時訪問同乙個共享資源,併發訪問帶來的問題就是競爭。2 什麼是競爭?答 linux 是多工作業系統,肯定存在多個任務共同操作同一段記憶體或裝置,這些任務...

併發之競爭狀態

如果兩個或者多個 goroutine 在沒有互相同步的情況下,訪問某個共享的資源,並試圖同時讀和寫這個資源,就處於相互競爭的狀態。這種情況被稱作競爭狀態。要想解決競爭狀態,對乙個共享資源的讀和寫操作必須是原子化的。也就是說,同一時刻只能有乙個 goroutine 對共享資源進行讀和寫操作。可見 為 ...

golang中的併發競爭態

golang程式中併發會引起併發競爭,一起沒理解,為什麼說兩個goroutine訪問共享資源會引發競爭態,我的理解如果只使用乙個邏輯處理器本質上不就是同一時間只有乙個goroutine在跑嗎,為什麼會引發競爭態呢,做了如下實驗,確實存在競爭態 package main import fmt runt...