www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁 > 嵌入式 > 嵌入式軟件
[導(dǎo)讀]本文討論了 Linux 內(nèi)核中可用的大量同步或鎖定機制。這些機制為 2.6.23 版內(nèi)核的許多可用方法提供了應(yīng)用程序接口(API)。但是在深入學(xué)習(xí) API 之前,首先需要明白將要解決的問題。

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

并發(fā)和鎖定

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

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

Linux 內(nèi)核在兩種模式中都支持并發(fā)。內(nèi)核本身是動態(tài)的,而且有許多創(chuàng)建競態(tài)條件的方法。Linux 內(nèi)核也支持多處理(multiprocessing),稱為對稱多處理(SMP)。

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

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

表 1. 并發(fā)中的重要定義

術(shù)語定義

競態(tài)條件兩個或更多線程同時操作資源時將會導(dǎo)致不一致的結(jié)果。

臨界段用于協(xié)調(diào)對共享資源的訪問的代碼段。

互斥鎖確保對共享資源進行排他訪問的軟件特性。

死鎖由兩個或更多進程和資源鎖導(dǎo)致的一種特殊情形,將會降低進程的工作效率。

Linux 同步方法

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

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

原子操作

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

 

舊的鎖定方法

在內(nèi)核中實現(xiàn)鎖定的一種不太好的方法是通過禁用本地 CPU 的硬中斷。這些函數(shù)均可用并且仍得到使用(有時用于原子運算符),但我們并不推薦使用。local_irq_save 例程禁用中斷,而 local_irq_restore 恢復(fù)以前啟用過的中斷。這些例程都是可重入的(reentrant),也就是說它們可以在其他例程上下文中被調(diào)用。

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

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

清單 1. 創(chuàng)建和初始化原子變量

atomic_t my_counter ATOMIC_INIT(0);

... or ...

atomic_set( &my_counter, 0 );

原子 API 支持一個涵蓋許多用例的富函數(shù)集??梢允褂?atomic_read 讀取原子變量中的內(nèi)容,也可以使用 atomic_add 為一個變量添加指定值。最常用的操作是使用 atomic_inc 使變量遞增。也可用減號運算符,它的作用與相加和遞增操作相反。清單 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 例程。這些例程允許對原子變量進行操縱和測試(作為一個原子操作來執(zhí)行)。一個叫做 atomic_add_negative 的特殊函數(shù)被添加到原子變量中,然后當(dāng)結(jié)果值為負(fù)數(shù)時返回真(true)。這被內(nèi)核中一些依賴于架構(gòu)的信號量函數(shù)使用。

許多函數(shù)都不返回變量的值,但兩個函數(shù)除外。它們會返回結(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_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 ));

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

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

清單 4. 位掩碼原子函數(shù)

unsigned long my_bitmask;

atomic_clear_mask( 0, &my_bitmask );

atomic_set_mask( (1<<24), &my_bitmask );

 

原子 API 原型

原子操作依賴于架構(gòu),可以在 ./linux/include/asm-/atomic.h 中找到。

自旋鎖

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

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

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

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

清單 5. 創(chuàng)建和初始化自旋鎖

spinlock_t my_spinlock = SPIN_LOCK_UNLOCKED;

... or ...

DEFINE_SPINLOCK( my_spinlock );

... or ...

spin_lock_init( &my_spinlock );

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

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

清單 6. 自旋鎖 lock 和 unlock 函數(shù)

spin_lock( &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_unlock_irqrestore( &my_spinlock, flags );

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

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

清單 8. 自旋鎖函數(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)建就是為了支持這種模型。這個模型有趣的地方在于允許多個線程同時訪問相同數(shù)據(jù),但同一時刻只允許一個線程寫入數(shù)據(jù)。如果執(zhí)行寫操作的線程持有此鎖,則臨界段不能由其他線程讀取。如果一個執(zhí)行讀操作的線程持有此鎖,那么多個讀線程都可以進入臨界段。清單 9 演示了這個模型。

清單 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)對讀/寫自旋鎖進行了修改。顯然,如果您使用的是原版的讀/寫鎖,那么按照標(biāo)準(zhǔn)自旋鎖的用法使用這個自旋鎖,而不區(qū)分讀線程和寫線程。

內(nèi)核互斥鎖

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

可以通過 DEFINE_MUTEX 宏使用一個操作創(chuàng)建和初始化互斥鎖。這將創(chuàng)建一個新的互斥鎖并初始化其結(jié)構(gòu)??梢栽?./linux/include/linux/mutex.h 中查看該實現(xiàn)。

DEFINE_MUTEX( my_mutex );

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

清單 10. 嘗試使用 mutex_trylock 獲得互斥鎖

ret = mutex_trylock( &my_mutex );

if (ret != 0) {

// Got the lock!

} else {

// Did not get the lock

}

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

清單 11. 鎖定一個可能處于休眠狀態(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)一個互斥鎖被鎖定后,它必須被解鎖。這是由 mutex_unlock 函數(shù)來完成的。這個函數(shù)不能從中斷上下文調(diào)用。最后,可以通過調(diào)用 mutex_is_locked 檢查互斥鎖的狀態(tài)。這個調(diào)用實際上編譯成一個內(nèi)聯(lián)函數(shù)。如果互斥鎖被持有(鎖定),那么就會返回 1;否則,返回 0。清單 12 演示了這些函數(shù)。

清單 12. 用 mutex_is_locked 測試互斥鎖鎖

mutex_unlock( &my_mutex );

if (mutex_is_locked( &my_mutex ) == 0) {

// Mutex is unlocked

}

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

大內(nèi)核鎖(Big kernel lock)

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

結(jié)束語

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

本站聲明: 本文章由作者或相關(guān)機構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點,本站亦不保證或承諾內(nèi)容真實性等。需要轉(zhuǎn)載請聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請及時聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫毥谦F公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

加利福尼亞州圣克拉拉縣2024年8月30日 /美通社/ -- 數(shù)字化轉(zhuǎn)型技術(shù)解決方案公司Trianz今天宣布,該公司與Amazon Web Services (AWS)簽訂了...

關(guān)鍵字: AWS AN BSP 數(shù)字化

倫敦2024年8月29日 /美通社/ -- 英國汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動 BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運行,同時企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險,如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報道,騰訊和網(wǎng)易近期正在縮減他們對日本游戲市場的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會開幕式在貴陽舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國國際大數(shù)據(jù)產(chǎn)業(yè)博覽會上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機 衛(wèi)星通信

要點: 有效應(yīng)對環(huán)境變化,經(jīng)營業(yè)績穩(wěn)中有升 落實提質(zhì)增效舉措,毛利潤率延續(xù)升勢 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競爭力 堅持高質(zhì)量發(fā)展策略,塑強核心競爭優(yōu)勢...

關(guān)鍵字: 通信 BSP 電信運營商 數(shù)字經(jīng)濟

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺與中國電影電視技術(shù)學(xué)會聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會上宣布正式成立。 活動現(xiàn)場 NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會上,軟通動力信息技術(shù)(集團)股份有限公司(以下簡稱"軟通動力")與長三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉