Linux,同步方法剖析-Linux,同步方法剖析內(nèi)核原子自旋鎖和互斥鎖x_第1頁
Linux,同步方法剖析-Linux,同步方法剖析內(nèi)核原子自旋鎖和互斥鎖x_第2頁
Linux,同步方法剖析-Linux,同步方法剖析內(nèi)核原子自旋鎖和互斥鎖x_第3頁
Linux,同步方法剖析-Linux,同步方法剖析內(nèi)核原子自旋鎖和互斥鎖x_第4頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、linux,同步方法剖析-linux,同步方法剖析內(nèi)核原子,自旋鎖和互斥鎖x linux 同步方法剖析-linux 同步方法剖析內(nèi)核原子,自旋鎖和互斥鎖 linux 同步方法剖析 -linux 同步方法剖析內(nèi)核原子,自旋鎖和互斥鎖 在學(xué)習(xí) linux 的過程中,您也許接觸過并發(fā)(concurrency)、臨界段(critical section)和鎖定,但是如何在內(nèi)核中使用這些概念呢?本文討論了 2.6 版內(nèi)核中可用的鎖定機(jī)制,包括原子運(yùn)算符(atomic operator)、自旋鎖(spinlock)、讀/寫鎖(reader/writer lock)和內(nèi)核信號量(kernel semapho

2、re)。 本文還探討了每種機(jī)制最適合應(yīng)用到哪些地方,以構(gòu)建安全高效的內(nèi)核代碼。 本文討論了 linux 內(nèi)核中可用的大量同步或鎖定機(jī)制。這些機(jī)制為 2.6.23 版內(nèi)核的許多可用方法提供了應(yīng)用程序接口(api)。但是在深入學(xué)習(xí) api 之前,首先需要明白將要解決的問題。 并發(fā)和鎖定 當(dāng)存在并發(fā)特性時(shí),必須使用同步方法。當(dāng)在同一時(shí)間段出現(xiàn)兩個(gè)或更多進(jìn)程并且這些進(jìn)程彼此交互(例如,共享相同的資源)時(shí),就存在 并發(fā) 現(xiàn)象。 在單處理器(uniprocessor,up)主機(jī)上可能發(fā)生并發(fā),在這種主機(jī)中多個(gè)線程共享同一個(gè) cpu 并且搶占(preemption)創(chuàng)建競態(tài)條件。 搶占 通過臨時(shí)中斷一個(gè)線程

3、以執(zhí)行另一個(gè)線程的方式來實(shí)現(xiàn) cpu 共享。 競態(tài)條件 發(fā)生在兩個(gè)或更多線程操縱一個(gè)共享數(shù)據(jù)項(xiàng)時(shí),其結(jié)果取決于執(zhí)行的時(shí)間。在多處理器(mp)計(jì)算機(jī)中也存在并發(fā),其中每個(gè)處理器中共享相同數(shù)據(jù)的線程同時(shí)執(zhí)行。注意在 mp 情況下存在真正的并行(parallelism),因?yàn)榫€程是同時(shí)執(zhí)行的。而在 up 情形中,并行是通過搶占創(chuàng)建的。兩種模式中實(shí)現(xiàn)并發(fā)都較為困難。 linux 內(nèi)核在兩種模式中都支持并發(fā)。內(nèi)核本身是動(dòng)態(tài)的,而且有許多創(chuàng)建競態(tài)條件的方法。linux 內(nèi)核也支持多處理(multiprocessing),稱為對稱多處理(smp)。可以在本文后面的 參考資料 部分學(xué)到更多關(guān)于 smp 的知識

4、。 臨界段概念是為解決競態(tài)條件問題而產(chǎn)生的。一個(gè) 臨界段 是一段不允許多路訪問的受保護(hù)的代碼。這段代碼可以操縱共享數(shù)據(jù)或共享服務(wù)(例如硬件外圍設(shè)備)。臨界段操作時(shí)堅(jiān)持互斥鎖(mutual exclusion)原則(當(dāng)一個(gè)線程處于臨界段中時(shí),其他所有線程都不能進(jìn)入臨界段)。 臨界段中需要解決的一個(gè)問題是死鎖條件??紤]兩個(gè)獨(dú)立的臨界段,各自保護(hù)不同的。每個(gè)資源擁有一個(gè)鎖,在本例中稱為 a 和 b。假設(shè)有兩個(gè)線程需要訪問這些資源,線程 x 獲取了鎖 a,線程 y 獲取了鎖 b。當(dāng)這些鎖都被持有時(shí),每個(gè)線程都試圖占有其他線程當(dāng)前持有的鎖(線程 x 想要鎖 b,線程 y 想要鎖 a)。這時(shí)候線程就被死鎖

5、了,因?yàn)樗鼈兌汲钟幸粋€(gè)鎖而且還想要其他鎖。一個(gè)簡單的解決方案就是總是按相同次序獲取鎖,從而使其中一個(gè)線程得以完成。還需要其他解決方案檢測這種情形。表 1 定義了此處用到的一些重要的并發(fā)術(shù)語。 表 1. 并發(fā)中的重要定義 術(shù)語 定義 競態(tài)條件 兩個(gè)或更多線程同時(shí)操作資源時(shí)將會(huì)導(dǎo)致不一致的結(jié)果。 臨界段 用于協(xié)調(diào)對共享資源的訪問的代碼段。 互斥鎖 確保對共享資源進(jìn)行排他訪問的軟件特性。 死鎖 由兩個(gè)或更多進(jìn)程和資源鎖導(dǎo)致的一種特殊情形,將會(huì)降低進(jìn)程的工作效率。 回頁首 linux 同步方法 如果您了解了一些基本理論并且明白了需要解決的問題,接下來將學(xué)習(xí) linux 支持并發(fā)和互斥鎖的各種方法。在以

6、前,互斥鎖是通過禁用中斷來提供的,但是這種形式的鎖定效率比較低(現(xiàn)在在內(nèi)核中仍然存在這種用法)。這種方法也不能進(jìn)行擴(kuò)展,而且不能保證其他處理器上的互斥鎖。 在以下關(guān)于鎖定機(jī)制的討論中,我們首先看一下原子運(yùn)算符,它可以保護(hù)簡單變量(計(jì)數(shù)器和位掩碼(bitmask)。然后介紹簡單的自旋鎖和讀/寫鎖,它們構(gòu)成了一個(gè) smp 架構(gòu)的忙等待鎖(busy-wait lock)覆蓋。最后,我們討論構(gòu)建在原子 api 上的內(nèi)核互斥鎖。 回頁首 原子操作 linux 中最簡單的同步方法就是原子操作。 原子 意味著臨界段被包含在 api 函數(shù)中。不需要額外的鎖定,因?yàn)?api 函數(shù)已經(jīng)包含了鎖定。由于 c 不能實(shí)

7、現(xiàn)原子操作,因此 linux 依靠底層架構(gòu)來提供這項(xiàng)功能。各種底層架構(gòu)存在很大差異,因此原子函數(shù)的實(shí)現(xiàn)方法也各不相同。一些方法完全通過匯編語言來實(shí)現(xiàn),而另一些方法依靠 c 語言并且使用 local_irq_save 和 local_irq_restore 禁用中斷。 當(dāng)需要保護(hù)的數(shù)據(jù)非常簡單時(shí),例如一個(gè)計(jì)數(shù)器,原子運(yùn)算符是種理想的方法。盡管原理簡單,原子 api 提供了許多針對不同情形的運(yùn)算符。下面是一個(gè)使用此 api 的示例。 要聲明一個(gè)原子變量(atomic variable),首先聲明一個(gè) atomic_t 類型的變量。這個(gè)結(jié)構(gòu)包含了單個(gè) int 元素。接下來,需確保您的原子變量使用 a

8、tomic_init 符號常量進(jìn)行了初始化。 在清單 1 的情形中,原子計(jì)數(shù)器被設(shè)置為 0。也可以使用 atomic_set function 在運(yùn)行時(shí)對原子變量進(jìn)行初始化。 清單 1. 創(chuàng)建和初始化原子變量 atomic_t my_counter atomic_init(0); . or . atomic_set( my_counter, 0 ); 原子 api 支持一個(gè)涵蓋許多用例的富函數(shù)集??梢允褂?atomic_read 讀取原子變量中的內(nèi)容,也可以使用 atomic_add 為一個(gè)變量添加指定值。最常用的操作是使用 atomic_inc 使變量遞增。也可用減號運(yùn)算符,它的作用與相加和

9、遞增操作相反。清單 2. 演示了這些函數(shù)。 清單 2. 簡單的算術(shù)原子函數(shù) val = atomic_read( my_counter ); atomic_add( 1, my_counter ); atomic_inc( my_counter ); atomic_sub( 1, my_counter ); atomic_dec( my_counter ); 該 api 也支持許多其他常用用例,包括 operate-and-test 例程。這些例程允許對原子變量進(jìn)行操縱和測試(作為一個(gè)原子操作來執(zhí)行)。一個(gè)叫做 atomic_add_negative 的特殊函數(shù)被添加到原子變量中,然后當(dāng)結(jié)果值

10、為負(fù)數(shù)時(shí)返回真(true)。這被內(nèi)核中一些依賴于架構(gòu)的信號量函數(shù)使用。 許多函數(shù)都不返回變量的值,但兩個(gè)函數(shù)除外。它們會(huì)返回結(jié)果值( atomic_add_return 和 atomic_sub_return ),如清單 3 所示。 清單 3. operate-and-test 原子函數(shù) if (atomic_sub_and_test( 1, my_counter ) / my_counter is zero if (atomic_dec_and_test( my_counter ) / my_counter is zero if (atomic_inc_and_test( my_counte

11、r ) / my_counter is zero if (atomic_add_negative( 1, my_counter ) / my_counter is less than zero val = atomic_add_return( 1, my_counter ); val = atomic_sub_return( 1, my_counter ); 如果您的架構(gòu)支持 64 位長類型( bits_per_long 是 64 的),那么 可以使用 long_t atomic 操作??梢栽?linux/include/asm-generic/atomic.h 中查看可用的長操作(long

12、operation)。 原子 api 還支持位掩碼(bitmask)操作。跟前面提到的算術(shù)操作不一樣,它只包含設(shè)置和清除操作。許多驅(qū)動(dòng)程序使用這些原子操作,特別是 scsi。位掩碼原子操作的使用跟算術(shù)操作存在細(xì)微的差別,因?yàn)槠渲兄挥袃蓚€(gè)可用的操作(設(shè)置掩碼和清除掩碼)。使用這些操作前,需要提供一個(gè)值和將要進(jìn)行操作的位掩碼,如清單 4 所示。 清單 4. 位掩碼原子函數(shù) unsigned long my_bitmask; atomic_clear_mask( 0, my_bitmask ); atomic_set_mask( (124), my_bitmask ); 自旋鎖 自旋鎖是使用忙等待鎖

13、來確?;コ怄i的一種特殊方法。如果鎖可用,則獲取鎖,執(zhí)行互斥鎖動(dòng)作,然后釋放鎖。如果鎖不可用,線程將忙等待該鎖,直到其可用為止。忙等待看起來效率低下,但它實(shí)際上比將線程休眠然后當(dāng)鎖可用時(shí)將其喚醒要快得多。 自旋鎖只在 smp 系統(tǒng)中才有用,但是因?yàn)槟拇a最終將會(huì)在 smp 系統(tǒng)上運(yùn)行,將它們添加到 up 系統(tǒng)是個(gè)明智的做法。 自旋鎖有兩種可用的形式:完全鎖(full lock)和讀寫鎖。 首先看一下完全鎖。 首先通過一個(gè)簡單的聲明創(chuàng)建一個(gè)新的自旋鎖。這可以通過調(diào)用 spin_lock_init 進(jìn)行初始化。清單 5 中顯示的每個(gè)變量都會(huì)實(shí)現(xiàn)相同的結(jié)果。 清單 5. 創(chuàng)建和初始化自旋鎖 spin

14、lock_t my_spinlock = spin_lock_unlocked; . or . define_spinlock( my_spinlock ); . or . spin_lock_init( my_spinlock ); 定義了自旋鎖之后,就可以使用大量的鎖定變量了。每個(gè)變量用于不同的上下文。 清單 6 中顯示了 spin_lock 和 spin_unlock 變量。這是一個(gè)最簡單的變量,它不會(huì)執(zhí)行中斷禁用,但是包含全部的內(nèi)存壁壘(memory barrier)。這個(gè)變量假定中斷處理程序和該鎖之間沒有交互。 清單 6. 自旋鎖 lock 和 unlock 函數(shù) spin_lock

15、( my_spinlock ); / critical section spin_unlock( my_spinlock ); 接下來是 irqsave 和 irqrestore 對,如清單 7 所示。 spin_lock_irqsave 函數(shù)需要自旋鎖,并且在本地處理器(在 smp 情形中)上禁用中斷。spin_unlock_irqrestore 函數(shù)釋放自旋鎖,并且(通過 flags 參數(shù))恢復(fù)中斷。 清單 7. 自旋鎖變量,其中禁用了本地 cpu 中斷 spin_lock_irqsave( my_spinlock, flags ); / critical section spin_unl

16、ock_irqrestore( my_spinlock, flags ); spin_lock_irqsave / spin_unlock_irqrestore 的一個(gè)不太安全的變體是 spin_lock_irq / spin_unlock_irq 。 我建議不要使用此變體,因?yàn)樗鼤?huì)假設(shè)中斷狀態(tài)。 最后,如果內(nèi)核線程通過 bottom half 方式共享數(shù)據(jù),那么可以使用自旋鎖的另一個(gè)變體。bottom half 方法可以將設(shè)備驅(qū)動(dòng)程序中的工作延遲到中斷處理后執(zhí)行。這種自旋鎖禁用了本地 cpu 上的軟中斷。這可以阻止 softirq、tasklet 和 bottom half 在本地 cpu

17、上運(yùn)行。這個(gè)變體如清單 8 所示。 清單 8. 自旋鎖函數(shù)實(shí)現(xiàn) bottom-half 交互 spin_lock_bh( my_spinlock ); / critical section spin_unlock_bh( my_spinlock ); 回頁首 讀/ 寫鎖 在許多情形下,對數(shù)據(jù)的訪問是由大量的讀和少量的寫操作來完成的(讀取數(shù)據(jù)比寫入數(shù)據(jù)更常見)。讀/寫鎖的創(chuàng)建就是為了支持這種模型。這個(gè)模型有趣的地方在于允許多個(gè)線程同時(shí)訪問相同數(shù)據(jù),但同一時(shí)刻只允許一個(gè)線程寫入數(shù)據(jù)。如果執(zhí)行寫操作的線程持有此鎖,則臨界段不能由其他線程讀取。如果一個(gè)執(zhí)行讀操作的線程持有此鎖,那么多個(gè)讀線程都可以進(jìn)入

18、臨界段。清單 9 演示了這個(gè)模型。 清單 9. 讀 / 寫自旋鎖函數(shù) rwlock_t my_rwlock; rwlock_init( my_rwlock ); write_lock( my_rwlock ); / critical section - can read and write write_unlock( my_rwlock ); read_lock( my_rwlock ); / critical section - can read only read_unlock( my_rwlock ); 根據(jù)對鎖的需求,還針對 bottom half 和中斷請求(irq)對讀/寫自旋鎖進(jìn)

19、行了修改。顯然,如果您使用的是原版的讀/寫鎖,那么按照標(biāo)準(zhǔn)自旋鎖的用法使用這個(gè)自旋鎖,而不區(qū)分讀線程和寫線程。 回頁首 內(nèi)核互斥鎖 在內(nèi)核中可以使用互斥鎖來實(shí)現(xiàn)信號量行為。內(nèi)核互斥鎖是在原子 api 之上實(shí)現(xiàn)的,但這對于內(nèi)核用戶是不可見的?;コ怄i很簡單,但是有一些規(guī)則必須.。同一時(shí)間只能有一個(gè)任務(wù)持有互斥鎖,而且只有這個(gè)任務(wù)可以對互斥鎖進(jìn)行解鎖。互斥鎖 不能進(jìn)行遞歸鎖定或解鎖,并且互斥鎖可能不能用于交互上下文。但是互斥鎖比當(dāng)前的內(nèi)核信號量選項(xiàng)更快,并且更加緊湊,因此如果它們滿足您的需求,那么它們將是您明智的選擇。 可以通過 define_mutex 宏使用一個(gè)操作創(chuàng)建和初始化互斥鎖。這將創(chuàng)建一

20、個(gè)新的互斥鎖并初始化其結(jié)構(gòu)??梢栽?./linux/include/linux/mutex.h 中查看該實(shí)現(xiàn)。 define_mutex( my_mutex ); 互斥鎖 api 提供了 5 個(gè)函數(shù):其中 3 個(gè)用于鎖定,一個(gè)用于解鎖,另一個(gè)用于測試互斥鎖。首先看一下鎖定函數(shù)。在需要立即鎖定以及希望在互斥鎖不可用時(shí)掌握控制的情形下,可以使用第一個(gè)函數(shù) mutex_trylock 。該函數(shù)如清單 10 所示。 清單 10. 嘗試使用 mutex_trylock 獲得互斥鎖 ret = mutex_trylock( my_mutex ); if (ret != 0) / got the lock!

21、 else / did not get the lock 如果想等待這個(gè)鎖,可以調(diào)用 mutex_lock 。這個(gè)調(diào)用在互斥鎖可用時(shí)返回,否則,在互斥鎖鎖可用之前它將休眠。無論在哪種情形中,當(dāng)控制被返回時(shí),調(diào)用者將持有互斥鎖。最后,當(dāng)調(diào)用者休眠時(shí)使用 mutex_lock_interruptible 。在這種情況下,該函數(shù)可能返回 -eintr 。清單 11 中顯示了這兩種調(diào)用。 清單 11. 鎖定一個(gè)可能處于休眠狀態(tài)的互斥鎖 mutex_lock( my_mutex ); / lock is now held by the caller. if (mutex_lock_interruptible( my_mutex ) != 0) / interrupted by a signal, no mutex held 當(dāng)一個(gè)互斥鎖被鎖定后,

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論