国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Linux > 正文

linux設備驅動第五篇:驅動中的并發與竟態

2024-06-28 13:21:09
字體:
來源:轉載
供稿:網友
linux設備驅動第五篇:驅動中的并發與竟態 2015-04-11 13:25 by 麥子熟了, ... 閱讀, ... 評論, 收藏, 編輯 綜述

在上一篇介紹了linux驅動的調試方法,這一篇介紹一下在驅動編程中會遇到的并發和竟態以及如何處理并發和競爭。

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

  • SMP(Symmetric Multi-PRocessing),對稱多處理結構。SMP是一種緊耦合、共享存儲的系統模型,它的特點是多個CPU使用共同的系統總線,因此可訪問共同的外設和存儲器。
  • 中斷。中斷可 打斷正在執行的進程,若中斷處理程序訪問進程正在訪問的資源,則競態也會發生。中斷也可能被新的更高優先級的中斷打斷,因此,多個中斷之間也可能引起并發而導致競態。
  • 內核進程的搶占。linux是可搶占的,所以一個內核進程可能被另一個高優先級的內核進程搶占。如果兩個進程共同訪問共享資源,就會出現竟態。

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

信號量與互斥鎖信號量其實就是一個整型值,其核心是一個想進入臨界區的進程將在相關信號量上調用 P; 如果信號量的值大于零, 這個值遞減 1 并且進程繼續. 相反,,如果信號量的值是 0 ( 或更小 ), 進程必須等待直到別人釋放信號量. 解鎖一個信號量通過調用 V 完成; 這個函數遞增信號量的值,,并且, 如果需要, 喚醒等待的進程。而當信號量的初始值為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的信號量為1DECLARE_MUTEX_LOCKED(name) //初始化信號量為0//常用操作DECLARE_MUTEX(mount_sem);down(&mount_sem); //獲取信號量...critical section    //臨界區...up(&mount_sem);    //釋放信號量
常用的down操作還有
// 類似down(),因為down()而進入休眠的進程不能被信號打斷,而因為down_interruptible()而進入休眠的進程能被信號打斷, // 信號也會導致該函數返回,此時返回值非0int down_interruptible(struct semaphore *sem);// 嘗試獲得信號量sem,若立即獲得,它就獲得該信號量并返回0,否則,返回非0.它不會導致調用者睡眠,可在中斷上下文使用int down_trylock(struct semaphore *sem);
Completions 機制完成量(completion)提供了一種比信號量更好的同步機制,它用于一個執行單元等待另一個執行單元執行完某事。
// 定義完成量struct completion my_completion; // 初始化completioninit_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);

自旋鎖若一個進程要訪問臨界資源,測試鎖空閑,則進程獲得這個鎖并繼續執行;若測試結果表明鎖扔被占用,進程將在一個小的循環內重復“測試并設置”操作,進行所謂的“自旋”,等待自旋鎖持有者釋放這個鎖。自旋鎖與互斥鎖類似,但是互斥鎖不能用在可能睡眠的代碼中,而自旋鎖可以用在可睡眠的代碼中,典型的應用是可以用在中斷處理函數中。自旋鎖的相關操作:
// 定義自旋鎖 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);  // 獲取自旋鎖,保護臨界區...  // 臨界區spin_unlock();  // 解鎖

自旋鎖持有期間內核的搶占將被禁止。自旋鎖可以保證臨界區不受別的CPU和本CPU內的搶占進程打擾,但是得到鎖的代碼路徑在執行臨界區的時候還可能受到中斷和底半部(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驅動編程中經常用到的鎖機制,下面講一些內核中其他的一些實現。不加鎖算法有時, 你可以重新打造你的算法來完全避免加鎖的需要.。許多讀者/寫者情況 -- 如果只有一個寫者 -- 常常能夠在這個方式下工作.。如果寫者小心使數據結構,由讀者所見的,是一直一致的,,有可能創建一個不加鎖的數據結構。在linux內核中就有一個通用的無鎖的環形緩沖實現,具體內容參考<linux/kfifo.h>。原子變量與位操作原子操作指的是在執行過程中不會被別的代碼路徑所中斷的操作。原子變量與位操作都是原子操作。以下是其相關操作介紹。
// 設置原子變量的值void atomic_set(atomic_t *v, int i);  // 設置原子變量的值為iatomic_t v = ATOMIC_INIT(0);  // 定義原子變量v,并初始化為0 // 獲取原子變量的值atomic_read(atomic_t *v);  // 返回原子變量的值 // 原子變量加/減void atomic_add(int i, atomic_t *v);  // 原子變量加ivoid atomic_sub(int i, atomic_t *v);  // 原子變量減i // 原子變量自增/自減void atomic_inc(atomic_t *v);  // 原子變量增加1void atomic_dec(atomic_t *v);  // 原子變量減少1 // 操作并測試:對原子變量進行自增、自減和減操作后(沒有加)測試其是否為0,為0則返回true,否則返回falseint 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); // 操作并返回: 對原子變量進行加/減和自增/自減操作,并返回新的值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);  位原子操作:// 設置位void set_bit(nr, void *addr);  // 設置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位 // 測試并操作:等同于執行test_bit(nr, void *addr)后再執行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鎖,讀執行單元不會被寫執行單元阻塞,即讀執行單元可以在寫執行單元對被seqlock鎖保護的共享資源進行寫操作時仍然可以繼續讀,而不必等待寫執行單元完成寫操作,寫執行單元也不需要等待所有讀執行單元完成讀操作才去進行寫操作。寫執行單元之間仍是互斥的。若讀操作期間,發生了寫操作,必須重新讀取數據。seqlock鎖必須要求被保護的共享資源不含有指針。
// 獲得順序鎖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() // 寫執行單元使用順序鎖的模式如下:write_seqlock(&seqlock_a);...  // 寫操作代碼塊write_sequnlock(&seqlock_a);  讀執行單元操作:// 讀開始:返回順序鎖sl當前順序號unsigned read_seqbegin(const seqlock_t *sl);read_seqbegin_irqsave(lock, flags) // 重讀:讀執行單元在訪問完被順序鎖sl保護的共享資源后需要調用該函數來檢查,在讀訪問期間是否有寫操作。若有寫操作,重讀int read_seqretry(const seqlock_t *sl, unsigned iv);read_seqretry_irqrestore(lock, iv, flags) // 讀執行單元使用順序鎖的模式如下:do{    seqnum = read_seqbegin(&seqlock_a);    // 讀操作代碼塊     ...}while(read_seqretry(&seqlock_a, seqnum));

讀取-拷貝-更新(RCU)讀取-拷貝-更新(RCU) 是一個高級的互斥方法,在合適的時候可以取得非常高的效率。RCU可以看作讀寫鎖的高性能版本,相比讀寫鎖,RCU的優點在于既允許多個讀執行單元同時訪問被保護的數據,又允許多個讀執行單元和多個寫執行單元同時訪問被保護的數據。但是RCU不能替代讀寫鎖,因為如果寫比較多時,對讀執行單元的性能提高不能彌補寫執行單元導致的損失。由于平時應用較少,所以不做多說。小結以上就是linux驅動編程中涉及的并發與競態的內容,下面做一個簡單的小結。現在的處理器基本上都是SMP類型的,而且在新的內核版本中,基本上都支持搶占式的操作,在linux中很多程序都是可重入的,要保護這些數據,就得使用不同的鎖機制。而鎖機制的基本操作過程其實大同小異的,聲明變量,上鎖,執行臨界區代碼,然后再解鎖。不同點在于,可以重入的限制不同,有的可以無限制重入,有的只允許異種操作重入,而有的是不允許重入操作的,有的可以在可睡眠代碼中使用,有的不可以在可睡眠代碼中使用。而在考慮不同的鎖機制的使用時,也要考慮CPU處理的效率問題,對于不同的代碼長度,不同的代碼執行時間,選擇一個好的鎖對CPU的良好使用有很大的影響,否則將造成浪費。之前在linux設備驅動第三篇:寫一個簡單的字符設備驅動中介紹了簡單的字符設備驅動,下一篇將介紹一些字符設備驅動中得高級操作。

第一時間獲得博客更新提醒,以及更多技術信息分享,歡迎關注個人微信公眾平臺:程序員互動聯盟(coder_online),掃一掃下方二維碼或搜索微信號coder_online即可關注,閱讀android,Chrome等多種熱門技術文章。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 普安县| 辽中县| 同德县| 剑川县| 宜兰市| 巴塘县| 宝坻区| 湘乡市| 商洛市| 马边| 浦县| 松原市| 鲁山县| 南安市| 镇沅| 白河县| 双城市| 通渭县| 潜山县| 合水县| 新余市| 称多县| 商河县| 陇南市| 西贡区| 汾阳市| 晋中市| 通道| 新乐市| 丹寨县| 江门市| 湖南省| 威宁| 邛崃市| 凉城县| 兖州市| 吉木萨尔县| 龙里县| 浦北县| 武冈市| 墨脱县|