一、前言
Linux內(nèi)核中有許多不同類型的鎖,它們都可以用來保護(hù)關(guān)鍵資源,以避免多個(gè)線程或進(jìn)程之間發(fā)生競爭條件,從而保護(hù)系統(tǒng)的穩(wěn)定性和可靠性。這些鎖的類型包括:互斥鎖(mutex)、讀寫鎖(rwlock)、自旋鎖(spinlock)和信號(hào)量(semaphore)。今天就給大家介紹一下Linux內(nèi)核中的各種鎖,以及我們?cè)趯?shí)際項(xiàng)目中該如何選擇使用哪個(gè)鎖。
二、幾種鎖的介紹
互斥鎖(mutex) 是最常用的鎖,它可以保護(hù)共享資源,使得在某個(gè)時(shí)刻只有一個(gè)線程或進(jìn)程可以訪問它。讀寫鎖(rwlock)則可以同時(shí)允許多個(gè)線程或進(jìn)程讀取共享資源,但只允許一個(gè)線程或進(jìn)程寫入它。自旋鎖(spinlock)可以用來保護(hù)共享資源,使得在某個(gè)時(shí)刻只有一個(gè)線程或進(jìn)程可以訪問它,但它會(huì)使線程或進(jìn)程“自旋”,直到獲得鎖為止。最后,信號(hào)量(semaphore)可以用來控制對(duì)共享資源的訪問,以保證其他線程或進(jìn)程可以安全地訪問它們。
讀寫鎖(rwlock) 是一種用于控制多線程訪問共享資源的同步機(jī)制。當(dāng)一個(gè)線程需要讀取共享資源時(shí),可以獲取讀取鎖,這樣其他線程就可以同時(shí)讀取該資源,而不會(huì)引發(fā)沖突。當(dāng)一個(gè)線程需要寫入共享資源時(shí),可以獲取寫入鎖,這樣其他線程就不能訪問該資源,從而保證數(shù)據(jù)的完整性和一致性。
自旋鎖(spinlock) 是一種簡單而有效的用于解決多線程同步問題的鎖。它是一種排他鎖,可以在多線程環(huán)境下保護(hù)共享資源,以防止多個(gè)線程同時(shí)對(duì)該資源進(jìn)行訪問。自旋鎖的基本原理是,當(dāng)一個(gè)線程試圖獲取鎖時(shí),它會(huì)不斷嘗試獲取鎖,直到成功為止。在這期間,線程不會(huì)進(jìn)入休眠狀態(tài),而是一直處于忙等待(busy-waiting)狀態(tài),這也就是自旋鎖的由來。
信號(hào)量(semaphore) 是一種常用的同步機(jī)制,它可以用來控制多個(gè)線程對(duì)共享資源的訪問。它有助于確保同一時(shí)間只有一個(gè)線程能夠訪問共享資源,從而避免資源沖突和競爭。信號(hào)量是一種整數(shù)計(jì)數(shù)器,用于跟蹤可用資源的數(shù)量。當(dāng)一個(gè)線程需要訪問共享資源時(shí),它首先必須獲取信號(hào)量,這會(huì)將信號(hào)量的計(jì)數(shù)器減少1,而當(dāng)它完成訪問共享資源后,它必須釋放信號(hào)量,以便其他線程也可以訪問共享資源。
四、互斥鎖(Mutex)
互斥鎖是最基本的鎖類型,在內(nèi)核中使用較為廣泛。它是一種二元鎖,只能同時(shí)有一個(gè)線程持有該鎖。當(dāng)一個(gè)線程請(qǐng)求該鎖時(shí),如果鎖已被占用,則線程會(huì)被阻塞直到鎖被釋放。互斥鎖的實(shí)現(xiàn)使用了原子操作,因此它的性能比較高,但也容易出現(xiàn)死鎖情況。
在內(nèi)核中,互斥鎖的定義如下:
structmutex{ raw_spinlock_twait_lock; structlist_headwait_list; structtask_struct*owner; intrecursion; #ifdefCONFIG_DEBUG_LOCK_ALLOC structlockdep_mapdep_map; #endif };
互斥鎖的使用非常簡單,通常只需要調(diào)用兩個(gè)函數(shù)即可完成:
voidmutex_init(structmutex*lock):函數(shù)用于初始化互斥鎖 voidmutex_lock(structmutex*lock):函數(shù)用于獲取互斥鎖 voidmutex_unlock(structmutex*lock):函數(shù)用于釋放互斥鎖
五、讀寫鎖(Reader-Writer Lock)
讀寫鎖是一種特殊的鎖類型,它允許多個(gè)線程同時(shí)讀取共享資源,但只允許一個(gè)線程寫入共享資源。讀寫鎖的實(shí)現(xiàn)使用了兩個(gè)計(jì)數(shù)器,分別記錄當(dāng)前持有鎖的讀線程數(shù)和寫線程數(shù)。
在內(nèi)核中,讀寫鎖的定義如下:
structrw_semaphore{ longcount; structlist_headwait_list; #ifdefCONFIG_DEBUG_LOCK_ALLOC structlockdep_mapdep_map; #endif };
讀寫鎖的使用也比較簡單,通常只需要調(diào)用三個(gè)函數(shù)即可完成:
init_rwsem(struct rw_semaphore *sem):函數(shù)用于初始化讀寫鎖 down_read(struct rw_semaphore *sem):函數(shù)用于獲取讀鎖 up_read(struct rw_semaphore *sem):函數(shù)用于釋放讀鎖 down_write(struct rw_semaphore *sem):函數(shù)用于獲取寫鎖 up_write(struct rw_semaphore *sem):函數(shù)用于釋放寫鎖
六、自旋鎖(spinlock)
自旋鎖是一種保護(hù)共享資源的鎖,它會(huì)在等待期間一直占用CPU。自旋鎖適用于代碼臨界區(qū)比較小的情況,且共享資源的獨(dú)占時(shí)間比較短,這樣就可以避免上下文切換的開銷。自旋鎖不能用于需要睡眠的代碼臨界區(qū),因?yàn)樵谒咂陂g自旋鎖會(huì)一直占用CPU。
在Linux內(nèi)核中,自旋鎖使用spinlock_t類型表示,可以通過spin_lock()和spin_unlock()函數(shù)對(duì)其進(jìn)行操作。
spin_lock_init(spinlock_t*lock):用于初始化自旋鎖,將自旋鎖的初始狀態(tài)設(shè)置為未加鎖狀態(tài)。 spin_lock(spinlock_t*lock):用于獲得自旋鎖,如果自旋鎖已經(jīng)被占用,則當(dāng)前進(jìn)程會(huì)自旋等待,直到自旋鎖可用。 spin_trylock(spinlock_t*lock):用于嘗試獲取自旋鎖,如果自旋鎖當(dāng)前被占用,則返回0,否則返回1。 spin_unlock(spinlock_t*lock):用于釋放自旋鎖。
在使用自旋鎖時(shí),需要注意以下幾點(diǎn):
自旋鎖只適用于臨界區(qū)代碼比較短的情況,因?yàn)樽孕却倪^程會(huì)占用CPU資源。
自旋鎖不可重入,也就是說,如果一個(gè)進(jìn)程已經(jīng)持有了自旋鎖,那么它不能再次獲取該自旋鎖。
在持有自旋鎖的情況下,應(yīng)該盡量避免調(diào)用可能會(huì)導(dǎo)致調(diào)度的內(nèi)核函數(shù),比如睡眠函數(shù),因?yàn)檫@可能會(huì)導(dǎo)致死鎖的發(fā)生。
在使用自旋鎖的時(shí)候,應(yīng)該盡量避免嵌套使用不同類型的鎖,比如自旋鎖和讀寫鎖,因?yàn)檫@可能會(huì)導(dǎo)致死鎖的發(fā)生。
當(dāng)臨界區(qū)代碼較長或者需要睡眠時(shí),應(yīng)該使用信號(hào)量或者讀寫鎖來代替自旋鎖。
七、信號(hào)量(semaphore)
信號(hào)量是一種更高級(jí)的鎖機(jī)制,它可以控制對(duì)共享資源的訪問次數(shù)。信號(hào)量可分為二元信號(hào)量和計(jì)數(shù)信號(hào)量。二元信號(hào)量只有0和1兩種狀態(tài),常用于互斥鎖的實(shí)現(xiàn);計(jì)數(shù)信號(hào)量則可以允許多個(gè)進(jìn)程同時(shí)訪問同一共享資源,只要它們申請(qǐng)信號(hào)量的數(shù)量不超過該資源所允許的最大數(shù)量。
在Linux內(nèi)核中,信號(hào)量使用struct semaphore結(jié)構(gòu)表示,可以通過down()和up()函數(shù)對(duì)其進(jìn)行操作。
voidsema_init(structsemaphore*sem,intval):初始化一個(gè)信號(hào)量,val參數(shù)表示初始值。 voiddown(structsemaphore*sem):嘗試獲取信號(hào)量,如果信號(hào)量值為0,調(diào)用進(jìn)程將被阻塞。 intdown_interruptible(structsemaphore*sem):嘗試獲取信號(hào)量,如果信號(hào)量值為0,調(diào)用進(jìn)程將被阻塞,并可以被中斷。 intdown_trylock(structsemaphore*sem):嘗試獲取信號(hào)量,如果信號(hào)量值為0,則立即返回,否則返回錯(cuò)誤。 voidup(structsemaphore*sem):釋放信號(hào)量,將信號(hào)量的值加 1,并喚醒可能正在等待信號(hào)量的進(jìn)程。
八、該如何選擇正確的鎖
當(dāng)需要對(duì)共享資源進(jìn)行訪問和修改時(shí),我們通常需要采用同步機(jī)制來保證數(shù)據(jù)的一致性和正確性,其中鎖是最基本的同步機(jī)制之一。不同類型的鎖適用于不同的場景。
互斥鎖適用于需要保護(hù)共享資源,只允許一個(gè)線程或進(jìn)程訪問共享資源的場景。例如,當(dāng)一個(gè)線程正在修改一個(gè)數(shù)據(jù)結(jié)構(gòu)時(shí),其他線程必須等待該線程釋放鎖后才能修改該數(shù)據(jù)結(jié)構(gòu)。
讀寫鎖適用于共享資源的讀寫操作頻繁且讀操作遠(yuǎn)大于寫操作的場景。讀寫鎖允許多個(gè)線程同時(shí)讀取共享資源,但只允許一個(gè)線程寫入共享資源。例如,在一個(gè)數(shù)據(jù)庫管理系統(tǒng)中,讀取操作比寫入操作頻繁,使用讀寫鎖可以提高系統(tǒng)的并發(fā)性能。
自旋鎖適用于保護(hù)共享資源的訪問時(shí)間很短的場景,當(dāng)線程需要等待的時(shí)間很短時(shí),自旋鎖比互斥鎖的性能更好。例如,在訪問共享資源時(shí)需要進(jìn)行一些簡單的操作,如對(duì)共享資源進(jìn)行遞增或遞減等操作。
信號(hào)量適用于需要協(xié)調(diào)多個(gè)線程或進(jìn)程對(duì)共享資源的訪問的場景,允許多個(gè)線程或進(jìn)程同時(shí)訪問共享資源,但同時(shí)訪問的線程或進(jìn)程數(shù)量有限。例如,在一個(gè)并發(fā)下載系統(tǒng)中,可以使用信號(hào)量來限制同時(shí)下載的文件數(shù)量。
舉個(gè)生活中的例子:當(dāng)我們?cè)谫I咖啡的時(shí)候,柜臺(tái)前可能會(huì)有一個(gè)小桶,上面寫著“請(qǐng)取走您需要的糖果,每人一顆”這樣的字樣。這個(gè)小桶就是一個(gè)信號(hào)量,它限制了每個(gè)人能夠取走的糖果的數(shù)量,從而保證了公平性。
如果我們把這個(gè)小桶換成互斥鎖,那么就可以只允許一個(gè)人在柜臺(tái)前取走糖果。如果使用讀寫鎖,那么在非高峰期的時(shí)候,多個(gè)人可以同時(shí)取走糖果,但在高峰期時(shí)只允許一個(gè)人取走。
而如果我們把這個(gè)小桶換成自旋鎖,那么當(dāng)有人在取走糖果時(shí),其他人就需要一直在那里等待,直到糖果被取走為止。這樣可能會(huì)造成浪費(fèi)時(shí)間的情況,因?yàn)槠渌丝赡苡懈o急的事情需要處理。
九、總結(jié)
在Linux內(nèi)核中,有四種常見的鎖:互斥鎖、讀寫鎖、自旋鎖和信號(hào)量。這些鎖適用于不同的場景,開發(fā)者需要根據(jù)實(shí)際情況選擇適當(dāng)?shù)逆i來確保并發(fā)訪問的正確性和性能。
審核編輯:劉清
-
計(jì)數(shù)器
+關(guān)注
關(guān)注
32文章
2256瀏覽量
94477 -
LINUX內(nèi)核
+關(guān)注
關(guān)注
1文章
316瀏覽量
21644 -
信號(hào)量
+關(guān)注
關(guān)注
0文章
53瀏覽量
8331
原文標(biāo)題:Linux內(nèi)核中的互斥鎖、讀寫鎖、自旋鎖、信號(hào)量該如何選擇?
文章出處:【微信號(hào):嵌入式悅翔園,微信公眾號(hào):嵌入式悅翔園】歡迎添加關(guān)注!文章轉(zhuǎn)載請(qǐng)注明出處。
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論