青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

牽著老婆滿街逛

嚴以律己,寬以待人. 三思而后行.
GMail/GTalk: yanglinbo#google.com;
MSN/Email: tx7do#yahoo.com.cn;
QQ: 3 0 3 3 9 6 9 2 0 .

Linux 同步方法剖析

在學習 Linux? 的過程中,您也許接觸過并發(concurrency)、臨界段(critical section)和鎖定,但是如何在內核中使用這些概念呢?本文討論了 2.6 版內核中可用的鎖定機制,包括原子運算符(atomic operator)、自旋鎖(spinlock)、讀/寫鎖(reader/writer lock)和內核信號量(kernel semaphore)。 本文還探討了每種機制最適合應用到哪些地方,以構建安全高效的內核代碼。

本文討論了 Linux 內核中可用的大量同步或鎖定機制。這些機制為 2.6.23 版內核的許多可用方法提供了應用程序接口(API)。但是在深入學習 API 之前,首先需要明白將要解決的問題。

并發和鎖定

當存在并發特性時,必須使用同步方法。當在同一時間段出現兩個或更多進程并且這些進程彼此交互(例如,共享相同的資源)時,就存在并發 現象。

在單處理器(uniprocessor,UP)主機上可能發生并發,在這種主機中多個線程共享同一個 CPU 并且搶占(preemption)創建競態條件。搶占 通過臨時中斷一個線程以執行另一個線程的方式來實現 CPU 共享。競態條件 發生在兩個或更多線程操縱一個共享數據項時,其結果取決于執行的時間。在多處理器(MP)計算機中也存在并發,其中每個處理器中共享相同數據的線程同時執行。注意在 MP 情況下存在真正的并行(parallelism),因為線程是同時執行的。而在 UP 情形中,并行是通過搶占創建的。兩種模式中實現并發都較為困難。

Linux 內核在兩種模式中都支持并發。內核本身是動態的,而且有許多創建競態條件的方法。Linux 內核也支持多處理(multiprocessing),稱為對稱多處理(SMP)。可以在本文后面的 參考資料 部分學到更多關于 SMP 的知識。

臨界段概念是為解決競態條件問題而產生的。一個臨界段 是一段不允許多路訪問的受保護的代碼。這段代碼可以操縱共享數據或共享服務(例如硬件外圍設備)。臨界段操作時堅持互斥鎖(mutual exclusion)原則(當一個線程處于臨界段中時,其他所有線程都不能進入臨界段)。

臨界段中需要解決的一個問題是死鎖條件。考慮兩個獨立的臨界段,各自保護不同的資源。每個資源擁有一個鎖,在本例中稱為 A 和 B。假設有兩個線程需要訪問這些資源,線程 X 獲取了鎖 A,線程 Y 獲取了鎖 B。當這些鎖都被持有時,每個線程都試圖占有其他線程當前持有的鎖(線程 X 想要鎖 B,線程 Y 想要鎖 A)。這時候線程就被死鎖了,因為它們都持有一個鎖而且還想要其他鎖。一個簡單的解決方案就是總是按相同次序獲取鎖,從而使其中一個線程得以完成。還需要其他解決方案檢測這種情形。表 1 定義了此處用到的一些重要的并發術語。


表 1. 并發中的重要定義
術語 定義
競態條件 兩個或更多線程同時操作資源時將會導致不一致的結果。
臨界段 用于協調對共享資源的訪問的代碼段。
互斥鎖 確保對共享資源進行排他訪問的軟件特性。
死鎖 由兩個或更多進程和資源鎖導致的一種特殊情形,將會降低進程的工作效率。

 

Linux 同步方法

如果您了解了一些基本理論并且明白了需要解決的問題,接下來將學習 Linux 支持并發和互斥鎖的各種方法。在以前,互斥鎖是通過禁用中斷來提供的,但是這種形式的鎖定效率比較低(現在在內核中仍然存在這種用法)。這種方法也不能進行擴展,而且不能保證其他處理器上的互斥鎖。

在以下關于鎖定機制的討論中,我們首先看一下原子運算符,它可以保護簡單變量(計數器和位掩碼(bitmask))。然后介紹簡單的自旋鎖和讀/寫鎖,它們構成了一個 SMP 架構的忙等待鎖(busy-wait lock)覆蓋。最后,我們討論構建在原子 API 上的內核互斥鎖。

原子操作

Linux 中最簡單的同步方法就是原子操作。原子 意味著臨界段被包含在 API 函數中。不需要額外的鎖定,因為 API 函數已經包含了鎖定。由于 C 不能實現原子操作,因此 Linux 依靠底層架構來提供這項功能。各種底層架構存在很大差異,因此原子函數的實現方法也各不相同。一些方法完全通過匯編語言來實現,而另一些方法依靠 c 語言并且使用 local_irq_savelocal_irq_restore 禁用中斷。

當需要保護的數據非常簡單時,例如一個計數器,原子運算符是種理想的方法。盡管原理簡單,原子 API 提供了許多針對不同情形的運算符。下面是一個使用此 API 的示例。

要聲明一個原子變量(atomic variable),首先聲明一個 atomic_t 類型的變量。這個結構包含了單個 int 元素。接下來,需確保您的原子變量使用 ATOMIC_INIT 符號常量進行了初始化。 在清單 1 的情形中,原子計數器被設置為 0。也可以使用 atomic_set 在運行時對原子變量進行初始化。


清單 1. 創建和初始化原子變量
atomic_t my_counter ATOMIC_INIT(0);
            ... or ...
            atomic_set( &my_counter, 0 );

原子 API 支持一個涵蓋許多用例的富函數集。可以使用 atomic_read 讀取原子變量中的內容,也可以使用 atomic_add 為一個變量添加指定值。最常用的操作是使用 atomic_inc 使變量遞增。也可用減號運算符,它的作用與相加和遞增操作相反。清單 2. 演示了這些函數。


清單 2. 簡單的算術原子函數
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 例程。這些例程允許對原子變量進行操縱和測試(作為一個原子操作來執行)。一個叫做 atomic_add_negative 的特殊函數被添加到原子變量中,然后當結果值為負數時返回真(true)。這被內核中一些依賴于架構的信號量函數使用。

許多函數都不返回變量的值,但兩個函數除外。它們會返回結果值( atomic_add_returnatomic_sub_return),如清單 3所示。


清單 3. Operate-and-test 原子函數
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_counter )) {
            // 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 ));

如果您的架構支持 64 位長類型(BITS_PER_LONG 是 64 的),那么可以使用 long_t atomic 操作。可以在 linux/include/asm-generic/atomic.h 中查看可用的長操作(long operation)。

原子 API 還支持位掩碼(bitmask)操作。跟前面提到的算術操作不一樣,它只包含設置和清除操作。許多驅動程序使用這些原子操作,特別是 SCSI。位掩碼原子操作的使用跟算術操作存在細微的差別,因為其中只有兩個可用的操作(設置掩碼和清除掩碼)。使用這些操作前,需要提供一個值和將要進行操作的位掩碼,如清單 4 所示。


清單 4. 位掩碼原子函數
unsigned long my_bitmask;
            atomic_clear_mask( 0, &my_bitmask );
            atomic_set_mask( (1<<24), &my_bitmask );

自旋鎖

自旋鎖是使用忙等待鎖來確保互斥鎖的一種特殊方法。如果鎖可用,則獲取鎖,執行互斥鎖動作,然后釋放鎖。如果鎖不可用,線程將忙等待該鎖,直到其可用為止。忙等待看起來效率低下,但它實際上比將線程休眠然后當鎖可用時將其喚醒要快得多。

自旋鎖只在 SMP 系統中才有用,但是因為您的代碼最終將會在 SMP 系統上運行,將它們添加到 UP 系統是個明智的做法。

自旋鎖有兩種可用的形式:完全鎖(full lock)和讀寫鎖。 首先看一下完全鎖。

首先通過一個簡單的聲明創建一個新的自旋鎖。這可以通過調用 spin_lock_init 進行初始化。清單 5 中顯示的每個變量都會實現相同的結果。


清單 5. 創建和初始化自旋鎖

spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;
            ... or ...
            DEFINE_SPINLOCK( my_spinlock );
            ... or ...
            spin_lock_init( &my_spinlock );

 

定義了自旋鎖之后,就可以使用大量的鎖定變量了。每個變量用于不同的上下文。

清單 6 中顯示了 spin_lockspin_unlock 變量。這是一個最簡單的變量,它不會執行中斷禁用,但是包含全部的內存壁壘(memory barrier)。這個變量假定中斷處理程序和該鎖之間沒有交互。


清單 6. 自旋鎖 lock 和 unlock 函數

spin_lock( &my_spinlock );
            // critical section
            spin_unlock( &my_spinlock );

 

接下來是 irqsaveirqrestore 對,如清單 7 所示。spin_lock_irqsave 函數需要自旋鎖,并且在本地處理器(在 SMP 情形中)上禁用中斷。spin_unlock_irqrestore 函數釋放自旋鎖,并且(通過 flags 參數)恢復中斷。


清單 7. 自旋鎖變量,其中禁用了本地 CPU 中斷

spin_lock_irqsave( &my_spinlock, flags );
            // critical section
            spin_unlock_irqrestore( &my_spinlock, flags );

 

spin_lock_irqsave/spin_unlock_irqrestore 的一個不太安全的變體是 spin_lock_irq/spin_unlock_irq。 我建議不要使用此變體,因為它會假設中斷狀態。

最后,如果內核線程通過 bottom half 方式共享數據,那么可以使用自旋鎖的另一個變體。bottom half 方法可以將設備驅動程序中的工作延遲到中斷處理后執行。這種自旋鎖禁用了本地 CPU 上的軟中斷。這可以阻止 softirq、tasklet 和 bottom half 在本地 CPU 上運行。這個變體如清單 8 所示。


清單 8. 自旋鎖函數實現 bottom-half 交互

spin_lock_bh( &my_spinlock );
            // critical section
            spin_unlock_bh( &my_spinlock );

讀/寫鎖

在許多情形下,對數據的訪問是由大量的讀和少量的寫操作來完成的(讀取數據比寫入數據更常見)。讀/寫鎖的創建就是為了支持這種模型。這個模型有趣的地方在于允許多個線程同時訪問相同數據,但同一時刻只允許一個線程寫入數據。如果執行寫操作的線程持有此鎖,則臨界段不能由其他線程讀取。如果一個執行讀操作的線程持有此鎖,那么多個讀線程都可以進入臨界段。清單 9 演示了這個模型。


清單 9. 讀/寫自旋鎖函數
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 );

根據對鎖的需求,還針對 bottom half 和中斷請求(IRQ)對讀/寫自旋鎖進行了修改。顯然,如果您使用的是原版的讀/寫鎖,那么按照標準自旋鎖的用法使用這個自旋鎖,而不區分讀線程和寫線程。

內核互斥鎖

在內核中可以使用互斥鎖來實現信號量行為。內核互斥鎖是在原子 API 之上實現的,但這對于內核用戶是不可見的。互斥鎖很簡單,但是有一些規則必須牢記。同一時間只能有一個任務持有互斥鎖,而且只有這個任務可以對互斥鎖進行解鎖。互斥鎖不能進行遞歸鎖定或解鎖,并且互斥鎖可能不能用于交互上下文。但是互斥鎖比當前的內核信號量選項更快,并且更加緊湊,因此如果它們滿足您的需求,那么它們將是您明智的選擇。

可以通過 DEFINE_MUTEX 宏使用一個操作創建和初始化互斥鎖。這將創建一個新的互斥鎖并初始化其結構。可以在 ./linux/include/linux/mutex.h 中查看該實現。

DEFINE_MUTEX( my_mutex );

互斥鎖 API 提供了 5 個函數:其中 3 個用于鎖定,一個用于解鎖,另一個用于測試互斥鎖。首先看一下鎖定函數。在需要立即鎖定以及希望在互斥鎖不可用時掌握控制的情形下,可以使用第一個函數 mutex_trylock。該函數如清單 10 所示。


清單 10. 嘗試使用 mutex_trylock 獲得互斥鎖
ret = mutex_trylock( &my_mutex );
            if (ret != 0) {
            // Got the lock!
            } else {
            // Did not get the lock
            }

如果想等待這個鎖,可以調用 mutex_lock。這個調用在互斥鎖可用時返回,否則,在互斥鎖鎖可用之前它將休眠。無論在哪種情形中,當控制被返回時,調用者將持有互斥鎖。最后,當調用者休眠時使用 mutex_lock_interruptible。在這種情況下,該函數可能返回 -EINTR。清單 11 中顯示了這兩種調用。


清單 11. 鎖定一個可能處于休眠狀態的互斥鎖
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
            }

當一個互斥鎖被鎖定后,它必須被解鎖。這是由 mutex_unlock 函數來完成的。這個函數不能從中斷上下文調用。最后,可以通過調用 mutex_is_locked 檢查互斥鎖的狀態。這個調用實際上編譯成一個內聯函數。如果互斥鎖被持有(鎖定),那么就會返回 1;否則,返回 0。清單 12 演示了這些函數。


清單 12. 用 mutex_is_locked 測試互斥鎖鎖
mutex_unlock( &my_mutex );
            if (mutex_is_locked( &my_mutex ) == 0) {
            // Mutex is unlocked
            }

互斥鎖 API 存在著自身的局限性,因為它是基于原子 API 的。但是其效率比較高,如果能滿足你的需要,還是可以使用的。

大內核鎖(Big kernel lock)

最后看一下大內核鎖(BLK)。它在內核中的用途越來越小,但是仍然有一些保留下來的用法。BKL 使多處理器 Linux 成為可能,但是細粒度(finer-grained)鎖正在慢慢取代 BKL。BKL 通過 lock_kernelunlock_kernel 函數提供。要獲得更多信息,請查看 ./linux/lib/kernel_lock.c。

結束語

Linux 性能非凡,其鎖定方法也一樣。原子鎖不僅提供了一種鎖定機制,同時也提供了算術或 bitwise 操作。自旋鎖提供了一種鎖定機制(主要應用于 SMP),而且讀/寫自旋鎖允許多個讀線程且僅有一個寫線程獲得給定的鎖。最后,互斥鎖是一種新的鎖定機制,提供了一種構建在原子之上的簡單 API。不管你需要什么,Linux 都會提供一種鎖定方案保護您的數據。

posted on 2008-01-25 11:26 楊粼波 閱讀(268) 評論(0)  編輯 收藏 引用

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            欧美sm视频| 国产精品成人aaaaa网站| 欧美承认网站| 免费高清在线一区| 久久亚洲美女| 欧美aⅴ99久久黑人专区| 久久综合久久综合这里只有精品 | 久久综合九色欧美综合狠狠| 久久一综合视频| 欧美成人乱码一区二区三区| 欧美激情性爽国产精品17p| 亚洲国产精品福利| 亚洲三级国产| 香蕉成人久久| 麻豆视频一区二区| 欧美日韩精品不卡| 国产亚洲一区二区精品| 影音先锋在线一区| 亚洲一区二区三区激情| 久久综合久久综合九色| 欧美激情国产精品| 亚洲免费视频网站| 免费亚洲电影在线| 亚洲美女色禁图| 久久久精品视频成人| 欧美人与性动交α欧美精品济南到| 欧美性猛交99久久久久99按摩 | 欧美日韩精品一区二区天天拍小说 | 99一区二区| 先锋影音国产精品| 欧美高清视频| 欧美在线观看视频一区二区三区| 久久躁狠狠躁夜夜爽| 国产精品高潮视频| 亚洲精品影视| 乱人伦精品视频在线观看| 亚洲精品婷婷| 久久视频这里只有精品| 国产精品区一区二区三区| 亚洲国产精品激情在线观看| 亚洲调教视频在线观看| 亚洲高清视频的网址| 久久久成人网| 国产一区免费视频| 香蕉尹人综合在线观看| 亚洲精品乱码久久久久久蜜桃麻豆 | 欧美黄色aaaa| 久久精品国产77777蜜臀| 国产精品久久久久免费a∨大胸| 亚洲国产欧美一区二区三区同亚洲| 欧美一区二区三区四区夜夜大片| 亚洲国产欧美一区二区三区同亚洲 | 亚洲第一区在线| 久久美女性网| 欧美一级日韩一级| 国产欧美日韩一区二区三区在线观看 | 亚洲精品中文字幕有码专区| 国产亚洲精品综合一区91| 欧美三级第一页| 亚洲日本理论电影| 欧美激情1区| 欧美成人国产一区二区| 91久久精品日日躁夜夜躁欧美| 久久夜色精品国产欧美乱极品| 午夜老司机精品| 国产视频一区二区在线观看 | 欧美fxxxxxx另类| 久久一日本道色综合久久| 一区二区三区在线免费视频| 久久免费高清视频| 久久久久久久综合| 国产自产精品| 久久综合久久美利坚合众国| 久久久久网址| 亚洲精品日韩在线观看| 亚洲精品乱码久久久久久| 欧美看片网站| 亚洲永久网站| 羞羞漫画18久久大片| 国产一区二区三区精品欧美日韩一区二区三区 | 亚洲午夜久久久久久尤物| 国产精品你懂得| 欧美影院视频| 久久久久久久网站| 亚洲精品小视频| 亚洲天堂av在线免费| 国产精品第一区| 久久五月激情| 欧美日韩国产综合视频在线| 亚洲一区二区视频| 久久国产精品一区二区三区| 亚洲黄色视屏| 亚洲一区二区免费在线| 伊人久久综合| 艳女tv在线观看国产一区| 国产麻豆综合| 91久久精品美女| 国产偷自视频区视频一区二区| 欧美.com| 国产精品久久国产精品99gif| 久久久久久电影| 欧美精品一区二区久久婷婷| 亚洲综合视频在线| 免费不卡在线观看av| 亚洲——在线| 久久综合伊人77777麻豆| 亚洲欧美国产制服动漫| 乱码第一页成人| 欧美中文字幕在线播放| 女人天堂亚洲aⅴ在线观看| 欧美在线亚洲综合一区| 欧美一级专区免费大片| 欧美成人有码| 国产精品亚洲片夜色在线| 免费人成精品欧美精品| 国产精品视频免费一区| 欧美激情国产日韩| 国产一区二区你懂的| 日韩一区二区久久| 亚洲茄子视频| 午夜精品久久久久久久白皮肤| 99精品国产99久久久久久福利| 久久激情综合| 欧美一区二区日韩| 欧美四级剧情无删版影片| 亚洲电影在线免费观看| 精品动漫av| 欧美一级理论片| 欧美一区二视频| 国产精品久久国产愉拍| 亚洲精品亚洲人成人网| 亚洲精品一区在线| 欧美成人自拍| 亚洲国产婷婷香蕉久久久久久99| 国语自产精品视频在线看抢先版结局 | 国产精品丝袜xxxxxxx| 91久久精品一区二区别| 伊人久久av导航| 久久久久久9| 蜜桃久久av| 亚洲激情一区| 欧美剧在线免费观看网站| 亚洲精品美女免费| 一本大道久久a久久精品综合| 欧美激情精品久久久久久| 牛牛影视久久网| 亚洲肉体裸体xxxx137| 欧美成人嫩草网站| 亚洲精品综合久久中文字幕| 日韩一级在线| 国产精品videossex久久发布| 99在线|亚洲一区二区| 亚洲午夜一区二区三区| 国产精品日韩久久久久| 久久成人一区二区| 欧美成人自拍视频| 亚洲视频成人| 国产一区二区av| 麻豆精品视频在线观看| 亚洲精品国产日韩| 性色av一区二区三区红粉影视| 国产一区二区中文| 欧美aa在线视频| 在线一区免费观看| 麻豆久久婷婷| 亚洲视频一二区| 国产一区二区三区奇米久涩 | 久久精品国产精品| 91久久精品国产91久久性色| 欧美精品亚洲二区| 亚洲午夜在线视频| 亚洲三级免费观看| 91久久中文| 国产精品女主播一区二区三区| 欧美在线|欧美| 亚洲激情视频在线| 亚洲欧美另类中文字幕| 黄色日韩网站| 国产精品极品美女粉嫩高清在线| 欧美一区二区黄| 亚洲精品专区| 美女精品一区| 午夜精品剧场| 亚洲人成人77777线观看| 国产精品日韩欧美大师| 欧美二区不卡| 久久久久成人网| 中文日韩在线视频| 欧美粗暴jizz性欧美20| 欧美在线一二三| 一区二区日本视频| 亚洲高清资源| 国产亚洲精久久久久久| 亚洲第一区在线观看| 校园春色国产精品| 亚洲视频一二| 亚洲精品免费网站| 亚洲二区视频在线| 伊人久久亚洲热| 国产一区二区三区黄视频|