日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

设备驱动中的并发控制-自旋锁

發布時間:2024/1/8 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 设备驱动中的并发控制-自旋锁 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

????????在linux中提供了一些鎖機制來避免競爭,引入鎖的機制是因為單獨的原子操作不能滿足復雜的內核設計需求。Linux中一般可以認為有兩種鎖,一種是自旋鎖,另一種是信號量。這兩種鎖是為了解決內核中遇到的不同問題開發的。其實現機制和應用場合有所不同。

自旋鎖是一種簡單的并發控制機制,其是實現信號量和完成量的基礎。自旋鎖對資源有很好的保護作用。

????????自旋鎖的使用

在linux中,自旋鎖的類型為struct spinlock_t。內核提供了一系列的函數對struct spinlock_t進行操作。

1. 定義和初始化自旋鎖

????????spinlock_t lock; /* 定義自旋鎖 */

????????自旋鎖必須初始化才能被使用,對自旋鎖的初始化可以在編譯階段通過宏來實現。初始化自旋鎖可以使用宏SPIN_LOCK_UNLOCKED,這個宏表示一個沒有鎖定的自旋鎖。

????????spinlock_t lock = SPIN_LOCK_UNLOCKED; /* 初始化一個未使用的自旋鎖 */

????????在運行階段,可以使用spin_lock_init()函數動態的初始化一個自旋鎖:

????????void spin_lock_init(spinlock_t lock);

2. 鎖定自旋鎖

????????在進入臨界區前,需要使用spin_lock宏來獲得自旋鎖。spin_lock宏定義如下:

????????#define spin_lock(lock) ??_spin_lock(lock)

????????這個宏用來獲得lock自旋鎖,如果能夠立即獲得自旋鎖,則宏立刻返回;否則,這個鎖會一直自旋在那里,直到該鎖被其他線程釋放為止。

3. 釋放自旋鎖

????????當不在使用臨界區時,需要使用spin_unlock宏釋放自旋鎖。spin_unlock宏定義如下:

????????#define spin_unlock(lock) ??_spin_unlock(lock)

????????這個宏用來釋放lock自旋鎖,當調用該宏之后,鎖立刻被釋放。

4. 使用自旋鎖

????????最基本的自旋鎖 API 函數如下所示:

4.1 定義并初始化一個自旋鎖變量。

??????DEFINE_SPINLOCK(spinlock_t lock);
4.2 初始化自旋鎖:

????????int spin_lock_init(spinlock_t *lock);
4.3 獲取指定的自旋鎖,也叫做加鎖。

????????void spin_lock(spinlock_t *lock);

4.4釋放指定的自旋鎖。

????????void spin_unlock(spinlock_t *lock);
4.5 嘗試獲取指定的自旋鎖,如果沒有獲取到就返回 0

????????int spin_trylock(spinlock_t *lock);
4.6 檢查指定的自旋鎖是否被獲取,如果沒有被獲取就返回非 0,否則返回 0。

?????????int spin_is_locked(spinlock_t *lock);

????????自旋鎖API函數適用于SMP或支持搶占的單CPU下線程之間的并發訪問,也就是用于線程與線程之間。

????????使用自旋鎖的方法:

????????spinlock_t lock; ???????/* 定義自旋鎖 */

????????spin_lock_init(&lock); ??/* 初始化自旋鎖 */

????????spin_lock(&lock); ??????/* 獲得自旋鎖 */

????????/* 臨界資源 */

????????spin_unlock(&lock); ????/* 釋放自旋鎖 */

????????在驅動程序中,有些設備只允許打開一次,那么就需要一個自旋鎖保護表示設備的打開或關閉狀態的變量count。此處count屬于臨界資源,如果不對count進行保護,當設備頻繁打開時,可能會出現錯誤得count計數。使用自旋鎖包含count的代碼如下:

int count=0; spinlock_t lock; int xxx_int(void) {...spin_lock_init(&lock);... } /* 文件打開函數 */ int xxx_open(struct inode *inode, struct file *filp) {...spin_lock(&lock);if(count){spin_unlock(&lock);return -RBUSY;}count++;spin_unlock(&lock);... } /* 文件釋放函數 */ int xxx_release(struct inode *inode, struct file *filp) {... spin_lock(&lock);count--;spin_unlock(&lock);... }

5. 死鎖

????????兩種死鎖場景:

5.1 由睡眠引起的死鎖

????????被自旋鎖保護的臨界區一定不能調用任何能夠引起睡眠和阻塞的API函數,否則的話會可能會導致死鎖現象的發生。自旋鎖會自動禁止搶占,也就說當線程A得到鎖以后會暫時禁止內核搶占。如果線程 A 在持有鎖期間進入了休眠狀態,那么線程 A 會自動放棄 CPU 使用權。線程 B 開始運行,線程 B 也想要獲取鎖,但是此時鎖被 A 線程持有,而且內核搶占還被禁止了。線程 B 無法被調度出去,那么線程 A 就無法運行,鎖也就無法釋放,死鎖發生了。

5.2 由中斷引起的死鎖

????????如果線程 A 時運行,中斷也想訪問共享資源,那該怎么辦呢?首先可以肯定的是,中斷里面可以使用自旋鎖,但是在中斷里面使用自旋鎖的時候,在獲取鎖之前一定要先禁止本地中斷(也就是本 CPU 中斷,對于多核 SOC來說會有多個 CPU 核),否則可能導致鎖死現象的發生,如圖下所示:

?????????在圖中,線程 A 先運行,并且獲取到了 lock 這個鎖,當線程 A 運行 functionA 函數的時候中斷發生了,中斷搶走了 CPU 使用權。右邊的中斷服務函數也要獲取 lock 這個鎖,但是這個鎖被線程 A 占有著,中斷就會一直自旋,等待鎖有效。但是在中斷服務函數執行完之前,線程 A 是不可能執行的,場面就這么僵持著,死鎖發生。

5.3 解決死鎖問題

????????最好的解決方法就是獲取鎖之前關閉本地中斷,Linux 內核提供了相應的 API 函數,如下所示:

5.3.1 禁止本地中斷,并獲取自旋鎖。

????????void spin_lock_irq(spinlock_t *lock);

5.3.2 激活本地中斷,并釋放自旋鎖。

????????void spin_unlock_irq(spinlock_t *lock);

5.3.3 保存中斷狀態,禁止本地中斷,并獲取自旋鎖。

????????void spin_lock_irqsave(spinlock_t *lock, unsigned long flags)

5.3.4 將中斷狀態恢復到以前的狀態,并且激活本地中斷,釋放自旋鎖。

????????void spin_unlock_irqrestore(spinlock_t *lock, unsigned long flags)

????????使用 spin_lock_irq/spin_unlock_irq 的時候需要用戶能夠確定加鎖之前的中斷狀態,但實際上內核很龐大,運行也是“千變萬化”,我們是很難確定某個時刻的中斷狀態,因此不推薦使用

spin_lock_irq/spin_unlock_irq。建議使用 spin_lock_irqsave/spin_unlock_irqrestore,因為這一組函數會保存中斷狀態,在釋放鎖的時候會恢復中斷狀態。一般在線程中使用 spin_lock_irqsave/

spin_unlock_irqrestore,在中斷中使用 spin_lock/spin_unlock,示例代碼如下所示:

/* 線程 A */ void functionA (){unsigned long flags; /* 中斷狀態 */spin_lock_irqsave(&lock, flags) /* 獲取鎖 *//* 臨界區 */spin_unlock_irqrestore(&lock, flags) /* 釋放鎖 */ }/* 中斷服務函數 */ void irq() {spin_lock(&lock) /* 獲取鎖 *//* 臨界區 */spin_unlock(&lock) /* 釋放鎖 */ }

6. 自旋鎖的使用注意事項

????????在使用自旋鎖時,需要注意以下幾項:

6.1 自旋鎖是一種忙等待。Linux中,自旋鎖當條件不滿足時,會一直不斷地循環條件是否被滿足。如果滿足就解鎖,繼續運行下面的代碼。這種忙等待機制對系統的性能是有影響的。所以應該注意使用自旋鎖時,自旋鎖不應該長時間的持有,它是一種適合短時間鎖定的輕量級的加鎖機制。

6.2 自旋鎖不能遞歸使用。這是因為自旋鎖被設計成在不同線程或函數之間同步。如果一個線程在已經持有自旋鎖時,其處于忙等待狀態,則已經沒有機會釋放自己持有的鎖了。如果這時在調用自身,則自旋鎖永遠沒有執行的機會了。所以類似下面的遞歸形式不能使用自旋鎖:

void A() {鎖定自旋鎖;A();鎖定自旋鎖; }

6.3 自旋鎖保護的臨界區內不能調用任何可能導致線程休眠的 API 函數,否則的話可能導致死鎖。

補充:

臨界資源:系統中某些資源一次只允許一個進程使用,稱這樣的資源為臨界資源或互斥資源或共享變量。

臨界區:指的是一個訪問臨界資源的程序片段。

還有其他類型的鎖,如順序鎖,待學習。

總結

以上是生活随笔為你收集整理的设备驱动中的并发控制-自旋锁的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。