真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

linux驅(qū)動中并發(fā)與竟態(tài)的示例分析

小編給大家分享一下linux驅(qū)動中并發(fā)與竟態(tài)的示例分析,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!

10年積累的成都網(wǎng)站制作、成都網(wǎng)站建設(shè)經(jīng)驗(yàn),可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認(rèn)識你,你也不認(rèn)識我。但先網(wǎng)站制作后付款的網(wǎng)站建設(shè)流程,更有麻陽免費(fèi)網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。

首先什么是并發(fā)與竟態(tài)呢?并發(fā)(concurrency)指的是多個執(zhí)行單元同時、并行被執(zhí)行。而并發(fā)的執(zhí)行單元對共享資源(硬件資源和軟件上的全局、靜態(tài)變量)的訪問則容易導(dǎo)致競態(tài)(race conditions)??赡軐?dǎo)致并發(fā)和竟態(tài)的情況有:

  • SMP(Symmetric Multi-Processing),對稱多處理結(jié)構(gòu)。SMP是一種緊耦合、共享存儲的系統(tǒng)模型,它的特點(diǎn)是多個CPU使用共同的系統(tǒng)總線,因此可訪問共同的外設(shè)和存儲器。 

  • 中斷。中斷可 打斷正在執(zhí)行的進(jìn)程,若中斷處理程序訪問進(jìn)程正在訪問的資源,則競態(tài)也會發(fā)生。中斷也可能被新的更高優(yōu)先級的中斷打斷,因此,多個中斷之間也可能引起并發(fā)而導(dǎo)致競態(tài)。

  • 內(nèi)核進(jìn)程的搶占。linux是可搶占的,所以一個內(nèi)核進(jìn)程可能被另一個高優(yōu)先級的內(nèi)核進(jìn)程搶占。如果兩個進(jìn)程共同訪問共享資源,就會出現(xiàn)竟態(tài)。

以上三種情況只有SMP是真正意義上的并行,而其他都是宏觀上的并行,微觀上的串行。但其都會引發(fā)對臨界共享區(qū)的競爭問題。而解決競態(tài)問題的途徑是保證對共享資源的互斥訪問,即一個執(zhí)行單元在訪問共享資源的時候,其他的執(zhí)行單元被禁止訪問。那么linux內(nèi)核中如何做到對對共享資源的互斥訪問呢?在linux驅(qū)動編程中,常用的解決并發(fā)與竟態(tài)的手段有信號量與互斥鎖,Completions 機(jī)制,自旋鎖(spin lock),以及一些其他的不使用鎖的實(shí)現(xiàn)方式。下面一一介紹。

信號量與互斥鎖

信號量其實(shí)就是一個整型值,其核心是一個想進(jìn)入臨界區(qū)的進(jìn)程將在相關(guān)信號量上調(diào)用 P; 如果信號量的值大于零, 這個值遞減 1 并且進(jìn)程繼續(xù). 相反,,如果信號量的值是 0 ( 或更小 ), 進(jìn)程必須等待直到別人釋放信號量. 解鎖一個信號量通過調(diào)用 V 完成; 這個函數(shù)遞增信號量的值,,并且, 如果需要, 喚醒等待的進(jìn)程。而當(dāng)信號量的初始值為1的時候,就變成了互斥鎖。

信號量的典型使用形式:

//聲明信號量
struct semaphore sem;

//初始化信號量
void sema_init(struct semaphore *sem, int val)
    //常用下面兩種形式
#define init_MUTEX(sem) sema_init(sem, 1)
#define init_MUTEX_LOCKED(sem) sema_init(sem, 0)
    //以下是初始化信號量的快捷方式,最常用的
DECLARE_MUTEX(name)    //初始化name的信號量為1
DECLARE_MUTEX_LOCKED(name) //初始化信號量為0

//常用操作
DECLARE_MUTEX(mount_sem);
down(&mount_sem); //獲取信號量
...
critical section    //臨界區(qū)
...
up(&mount_sem);    //釋放信號量

常用的down操作還有

// 類似down(),因?yàn)閐own()而進(jìn)入休眠的進(jìn)程不能被信號打斷,而因?yàn)閐own_interruptible()而進(jìn)入休眠的進(jìn)程能被信號打斷, 
// 信號也會導(dǎo)致該函數(shù)返回,此時返回值非0
int down_interruptible(struct semaphore *sem);
// 嘗試獲得信號量sem,若立即獲得,它就獲得該信號量并返回0,否則,返回非0.它不會導(dǎo)致調(diào)用者睡眠,可在中斷上下文使用
int down_trylock(struct semaphore *sem);

Completions 機(jī)制

完成量(completion)提供了一種比信號量更好的同步機(jī)制,它用于一個執(zhí)行單元等待另一個執(zhí)行單元執(zhí)行完某事。

// 定義完成量 struct completion my_completion;   // 初始化completion init_completion(&my_completion);   // 定義和初始化快捷方式: DECLEAR_COMPLETION(my_completion);   // 等待一個completion被喚醒 void wait_for_completion(struct completion *c);   // 喚醒完成量 void cmplete(struct completion *c); void cmplete_all(struct completion *c);

自旋鎖

若一個進(jìn)程要訪問臨界資源,測試鎖空閑,則進(jìn)程獲得這個鎖并繼續(xù)執(zhí)行;若測試結(jié)果表明鎖扔被占用,進(jìn)程將在一個小的循環(huán)內(nèi)重復(fù)“測試并設(shè)置”操作,進(jìn)行所謂的“自旋”,等待自旋鎖持有者釋放這個鎖。自旋鎖與互斥鎖類似,但是互斥鎖不能用在可能睡眠的代碼中,而自旋鎖可以用在可睡眠的代碼中,典型的應(yīng)用是可以用在中斷處理函數(shù)中。自旋鎖的相關(guān)操作:

// 定義自旋鎖 
spinlock_t spin; 
 
// 初始化自旋鎖
spin_lock_init(lock);
 
// 獲得自旋鎖:若能立即獲得鎖,它獲得鎖并返回,否則,自旋,直到該鎖持有者釋放
spin_lock(lock); 
 
// 嘗試獲得自旋鎖:若能立即獲得鎖,它獲得并返回真,否則立即返回假,不再自旋
spin_trylock(lock); 
 
// 釋放自旋鎖: 與spin_lock(lock)和spin_trylock(lock)配對使用
spin_unlock(lock); 
 
  自旋鎖的使用:
// 定義一個自旋鎖
spinlock_t lock;
spin_lock_init(&lock);
 
spin_lock(&lock);  // 獲取自旋鎖,保護(hù)臨界區(qū)
...  // 臨界區(qū)
spin_unlock();  // 解鎖


自旋鎖持有期間內(nèi)核的搶占將被禁止。自旋鎖可以保證臨界區(qū)不受別的CPU和本CPU內(nèi)的搶占進(jìn)程打擾,但是得到鎖的代碼路徑在執(zhí)行臨界區(qū)的時候還可能受到中斷和底半部(BH)的影響。為防止這種影響,需要用到自旋鎖的衍生:

spin_lock_irq() = spin_lock() + local_irq_disable()
spin_unlock_irq() = spin_unlock() + local_irq_enable()
spin_lock_irqsave() = spin_lock() + local_irq_save()
spin_unlock_irqrestore() = spin_unlock() + local_irq_restore()
spin_lock_bh() = spin_lock() + local_bh_disable()
spin_unlock_bh() = spin_unlock() + local_bh_enable()

其他的一些選擇

以上是linux驅(qū)動編程中經(jīng)常用到的鎖機(jī)制,下面講一些內(nèi)核中其他的一些實(shí)現(xiàn)。

不加鎖算法

有時, 你可以重新打造你的算法來完全避免加鎖的需要.。許多讀者/寫者情況 -- 如果只有一個寫者 -- 常常能夠在這個方式下工作.。如果寫者小心使數(shù)據(jù)結(jié)構(gòu),由讀者所見的,是一直一致的,,有可能創(chuàng)建一個不加鎖的數(shù)據(jù)結(jié)構(gòu)。在linux內(nèi)核中就有一個通用的無鎖的環(huán)形緩沖實(shí)現(xiàn),具體內(nèi)容參考。

原子變量與位操作

原子操作指的是在執(zhí)行過程中不會被別的代碼路徑所中斷的操作。原子變量與位操作都是原子操作。以下是其相關(guān)操作介紹。

// 設(shè)置原子變量的值
void atomic_set(atomic_t *v, int i);  // 設(shè)置原子變量的值為i
atomic_t v = ATOMIC_INIT(0);  // 定義原子變量v,并初始化為0
 
// 獲取原子變量的值
atomic_read(atomic_t *v);  // 返回原子變量的值
 
// 原子變量加/減
void atomic_add(int i, atomic_t *v);  // 原子變量加i
void atomic_sub(int i, atomic_t *v);  // 原子變量減i
 
// 原子變量自增/自減
void atomic_inc(atomic_t *v);  // 原子變量增加1
void atomic_dec(atomic_t *v);  // 原子變量減少1
 
// 操作并測試:對原子變量進(jìn)行自增、自減和減操作后(沒有加)測試其是否為0,為0則返回true,否則返回false
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);
 
// 操作并返回: 對原子變量進(jìn)行加/減和自增/自減操作,并返回新的值
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);
  位原子操作:
// 設(shè)置位
void set_bit(nr, void *addr);  // 設(shè)置addr地址的第nr位,即將位寫1
 
// 清除位
void clear_bit(nr, void *addr);  // 清除addr地址的第nr位,即將位寫0
 
// 改變位
void change_bit(nr, void *addr);  // 對addr地址的第nr位取反
 
// 測試位
test_bit(nr, void *addr); // 返回addr地址的第nr位
 
// 測試并操作:等同于執(zhí)行test_bit(nr, void *addr)后再執(zhí)行xxx_bit(nr, void *addr)
int test_and_set_bit(nr, void *addr);
int test_and_clear_bit(nr, void *addr);
int test_and_change_bit(nr, void *addr);
seqlock(順序鎖)

使用seqlock鎖,讀執(zhí)行單元不會被寫執(zhí)行單元阻塞,即讀執(zhí)行單元可以在寫執(zhí)行單元對被seqlock鎖保護(hù)的共享資源進(jìn)行寫操作時仍然可以繼續(xù)讀,而不必等待寫執(zhí)行單元完成寫操作,寫執(zhí)行單元也不需要等待所有讀執(zhí)行單元完成讀操作才去進(jìn)行寫操作。寫執(zhí)行單元之間仍是互斥的。若讀操作期間,發(fā)生了寫操作,必須重新讀取數(shù)據(jù)。seqlock鎖必須要求被保護(hù)的共享資源不含有指針。

// 獲得順序鎖
void write_seqlock(seqlock_t *sl);
int write_tryseqlock(seqlock_t *sl);
write_seqlock_irqsave(lock, flags)
write_seqlock_irq(lock)
write_seqlock_bh()
 
// 釋放順序鎖
void write_sequnlock(seqlock_t *sl);
write_sequnlock_irqrestore(lock, flags)
write_sequnlock_irq(lock)
write_sequnlock_bh()
 
// 寫執(zhí)行單元使用順序鎖的模式如下:
write_seqlock(&seqlock_a);
...  // 寫操作代碼塊
write_sequnlock(&seqlock_a);
  讀執(zhí)行單元操作:
// 讀開始:返回順序鎖sl當(dāng)前順序號
unsigned read_seqbegin(const seqlock_t *sl);
read_seqbegin_irqsave(lock, flags)
 
// 重讀:讀執(zhí)行單元在訪問完被順序鎖sl保護(hù)的共享資源后需要調(diào)用該函數(shù)來檢查,在讀訪問期間是否有寫操作。若有寫操作,重讀
int read_seqretry(const seqlock_t *sl, unsigned iv);
read_seqretry_irqrestore(lock, iv, flags)
 
// 讀執(zhí)行單元使用順序鎖的模式如下:
do{
    seqnum = read_seqbegin(&seqlock_a);
    // 讀操作代碼塊 
    ...
}while(read_seqretry(&seqlock_a, seqnum));
讀取-拷貝-更新(RCU)

讀取-拷貝-更新(RCU) 是一個高級的互斥方法,在合適的時候可以取得非常高的效率。RCU可以看作讀寫鎖的高性能版本,相比讀寫鎖,RCU的優(yōu)點(diǎn)在于既允許多個讀執(zhí)行單元同時訪問被保護(hù)的數(shù)據(jù),又允許多個讀執(zhí)行單元和多個寫執(zhí)行單元同時訪問被保護(hù)的數(shù)據(jù)。但是RCU不能替代讀寫鎖,因?yàn)槿绻麑懕容^多時,對讀執(zhí)行單元的性能提高不能彌補(bǔ)寫執(zhí)行單元導(dǎo)致的損失。由于平時應(yīng)用較少,所以不做多說。

以上是“l(fā)inux驅(qū)動中并發(fā)與竟態(tài)的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道!


當(dāng)前標(biāo)題:linux驅(qū)動中并發(fā)與竟態(tài)的示例分析
網(wǎng)站鏈接:http://weahome.cn/article/jpphsg.html

其他資訊

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部