1. 程式人生 > >管理訊號量、自旋鎖、原子變數函式介面>>Linux 裝置驅動程式

管理訊號量、自旋鎖、原子變數函式介面>>Linux 裝置驅動程式

文章目錄

接下來是這個,沒想到核心的知識還是挺緊湊,已經不再有上個月的對程式碼恐慌的狀態了;
繼續實現我的願望,結果要努力過了才知道;

[0x100] 程序競態特徵

  • 簡述定義:同一時間,不同程序對共享資源的訪問,由於次序不同導致結果與預期不一致的情況;
  • 共享資源:全域性變數、核心空間資料、堆分配 等;被稱為臨界區
  • 面臨問題:當某程序操作的共享資源時,減少或者阻止其他程序對相同資源訪問或者修改;

[0x200] 訊號量

  1. 程序訊號量 :基於進入臨界區訪問數量控制,每次進入減1,當訊號量為0 時,即為鎖定狀態;
  2. 讀寫訊號量 :基於程序對臨界區的執行的操作,確定是否需要阻塞等待上一個程序完成操作;

[0x210] 程序訊號量函式介面[struct semaphore]

[0x211] 初始化訊號量

#include <asm/semaphore.h>
void sema_init(struct semaphore * sem,int val);
//函式功能     :初始化 訊號量結構體
//args 1      : 定義的訊號量結構體指標;
//args 2      : 訊號量的初始數值 如何數值為1 則為互斥訊號量;
DECLARE_MUTEX(struct semaphore *);
DECLARE_MUTEX_LOCK(struct semaphore*);  

[0x212] 獲取與釋放訊號量

#include <asm/semaphore.h>
/*不達目的不罷休的獲取訊號量*/
void down(struct semaphore*);
/*如果沒有被中斷就一直等待獲取訊號量,如果程序獲取訊號量時,被中斷則返回 非零,處理結果-ERESTARTSYS 或者 -EINTR*/
int down_interruptible(struct semaphore *);
/*試一下不行就放棄的獲取訊號量,如果沒有獲取訊號量,則返回非零值*/
int down_trylock(struct semaphore *);
/*釋放訊號量*/
void up(struct semaphore *);

[0x213] 使用訊號量注意事項

  • 使用訊號量之前必須初始化,且初始化必須位於設備註冊之前;
  • 如果使用 特殊的 加鎖 需要檢查返回值,例如 down_interruptible(), 並對應適當的處理;
  • 如果需要執行過程結束前,必須執行 up()解除鎖定,否則訊號量狀態將永遠無法找回;

[0x220] 讀寫訊號量函式介面[struct rw_semaphore]

  • 只讀臨界區可以併發, 只寫臨界區不可併發;;
  • 獲取臨界區資源時,寫入者優先順序高於讀取者的優先順序

[0x221] 配置讀訊號量

#include <linux/rwsem.h>
//函式功能     :初始化讀寫訊號量結構體
void init_rwsem(struct rw_semaphore *);

/*定義讀鎖定狀態與釋放狀態*/
void down_read(struct rw_semaphore *);
/*不同於其它trylock 函式,當獲取許可權時返回非零值,無法獲取時返回0*/
int  down_read_trylock(struct rw_semaphore *);
/*讀訊號量解除鎖定的必須是由 down_read_* 鎖定的臨界區*/
void up_read(struct rw_semaphore *);

[0x222] 配置寫訊號量

#include <linux/rwsem.h>
//函式功能     :初始化讀寫訊號量結構體
void init_rwsem(struct rw_semaphore *);

/*定義寫鎖定狀態與釋放狀態*/
void down_write(struct rw_semaphore *);
/*不同於其它trylock 函式,當獲取許可權時返回非零值,無法獲取時返回0*/
int  down_write_trylock(struct rw_semaphore *);
/*寫訊號量解除鎖定的必須是由 down_write_* 鎖定的臨界區*/
void up_write(struct rw_semaphore *);
/*寫入完成且仍處於寫訊號量狀態鎖定時,呼叫該函式可以允許只讀程序進入臨界區訪問*/
void downgrade_write(struct rw_semaphore *);

[0x300] 自旋鎖

  1. 鎖定狀態,將使未獲取鎖的程序一直處於自旋的執行狀態,直到獲取鎖成功;
  2. 非搶佔單核CPU的自旋鎖操作為空,是為了防止程序陷入永遠自旋狀態;
  3. 自旋鎖必須用於原子上下文,且儘量避免鎖定耗時操作;
  4. 自動禁止 CPU的搶佔,防止當前造成死鎖;

[0x310]自旋鎖注意事項

[0x311] 可能造成死鎖的情況

  • 程序切換中斷前獲取的自旋鎖,之後中斷上下文需要獲取這個鎖時;
  • 程序上下文中,執行過程中互相鎖定了對方需要的臨界區資源;

[0x312] 減少可能死鎖的情況

  1. 嚴格規定鎖定與解鎖的順序;
  2. 固定上層函式執行加鎖解鎖動作,減少內層巢狀函式執行鎖定的可能;
  3. 減少實現大量精細顆粒的鎖定過程;
  4. 實現迴圈棧緩衝 #include <linux/kfifo.h>;

[0x320] 自旋鎖函式介面[struct spinlock]

[0x321] 初始化自旋鎖

#include <linux/spinlock.h>
/*方法1*/
spinlock_t pym_lock = SPIN_LOCKP_UNLOCKED;
/*方法2*/
void spin_lock_init(spinlock_t *lock);

[0x322] 獲取自旋鎖

#include <linux/spinlock.h>
/*不做任何操作獲取自旋鎖*/
void spin_lock(spinlock_t *lock);
/*獲取自旋鎖之前禁止IRQ中斷,並儲存之前的中斷狀態到flag中,如果自旋處於中斷上下文 必須用此函式*/
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags);
/*獲取自旋鎖之前禁止IRQ中斷,不儲存之前的中斷狀態,如果自旋處於中斷上下文 必須用此函式*/
void spin_lock_irq(spinlock_t *lock);
/*獲取自旋鎖之前禁止軟中斷*/
void spin_lock_bh(spinlock_t *lock);

[0x323] 釋放自旋鎖

/*不做任何操作釋放自旋鎖*/
#include <linux/spinlock.h>
void spin_unlock(spinlock_t *lock);
/*釋放自旋鎖之前恢復IRQ中斷,並恢復之前的中斷狀態從flag中,其中flags 必須是從spin_lock_irqsave中的返回引數*/
void spin_unlock_irqrestore(spinlock_t *lock,unsigned long flags);
/*釋放自旋鎖之前恢復IRQ中斷,不恢復之前的中斷狀態*/
void spin_unlock_irq(spinlock_t *lock);
/*釋放自旋鎖之前禁止軟中斷*/
void spin_unlock_bh(spinlock_t *lock);

[0x330] 讀寫自旋鎖函式介面[rwlock_t]

[0x331] 配置讀自旋鎖

#include <linux/spinlock.h>
/*初始化讀寫自旋鎖*/
void rwlock_init(rwlock_t *lock);

/*獲取讀自旋鎖*/
void read_lock(rwlock_t *lock);
/*獲取讀自旋鎖之前禁止IRQ中斷,並儲存之前的中斷狀態到flag中,如果自旋處於中斷上下文 必須用此函式*/
void read_lock_irqsave(rwlock_t *lock,unsigned long flags);
/*獲取讀自旋鎖之前禁止IRQ中斷,不儲存之前的中斷狀態,如果自旋處於中斷上下文 必須用此函式*/
void read_lock_irq(rwlock_t *lock);
/*獲取讀自旋鎖之前禁止軟中斷*/
void read_lock_bh(rwlock_t *lock);

/*釋放讀自旋鎖*/
void read_unlock(rwlock_t *lock);
/*釋放讀自旋鎖之前恢復IRQ中斷,並恢復之前的中斷狀態從flag中,其中flags 必須是從read_lock_irqsave中的返回引數*/
void read_unlock_irqrestore(rwlock_t *lock,unsigned long flags);
/*釋放讀自旋鎖之前恢復IRQ中斷,不恢復之前的中斷狀態*/
void read_unlock_irq(rwlock_t *lock);
/*釋放讀自旋鎖之前禁止軟中斷*/
void read_unlock_bh(rwlock_t *lock);

[0x332] 配置寫自旋鎖

#include <linux/spinlock.h>
/*初始化讀寫自旋鎖*/
void rwlock_init(rwlock_t *lock);

/*獲取寫自旋鎖*/
void write_lock(rwlock_t *lock);
/*獲取寫自旋鎖之前禁止IRQ中斷,並儲存之前的中斷狀態到flag中,如果自旋處於中斷上下文 必須用此函式*/
void write_lock_irqsave(rwlock_t *lock,unsigned long flags);
/*獲取寫自旋鎖之前禁止IRQ中斷,不儲存之前的中斷狀態,如果自旋處於中斷上下文 必須用此函式*/
void write_lock_irq(rwlock_t *lock);
/*獲取寫自旋鎖之前禁止軟中斷*/
void write_lock_bh(rwlock_t *lock);

/*釋放寫自旋鎖*/
void write_unlock(rwlock_t *lock);
/*釋放寫自旋鎖之前恢復IRQ中斷,並恢復之前的中斷狀態從flag中,其中flags 必須是從read_lock_irqsave中的返回引數*/
void write_unlock_irqrestore(rwlock_t *lock,unsigned long flags);
/*釋放寫自旋鎖之前恢復IRQ中斷,不恢復之前的中斷狀態*/
void write_unlock_irq(rwlock_t *lock);
/*釋放寫自旋鎖之前禁止軟中斷*/
void write_unlock_bh(rwlock_t *lock);

[0x400] 原子變數

  1. 用於對較小範圍的資源的操作單入,是一種替補鎖結構的策略,但並不是一種鎖;
  2. 通常是對atomic_t 這種小於 24位的int 值的操作;
  3. 由於操作過程可能只需要一個指令即可完成,不可分割 所以稱為原子變數;
  4. 原子變數不能作為整形數值進行傳遞;

[0x410] 原子變數函式介面

[0x411] 建立與獲取、增加、減少

#include <asm/atomic.h>
/*1. 初始化 共兩種方法*/
/*method 1 :巨集初始化原子變數結構 其中 val是需要初始化的數值*/
atomic_t i_val = ATOMIC_INIT(val)/*method 2 :函式初始化原子變數 其中 i_ptr是原子變數結構,val是需要初始化的數值*/
void atomic_set(atomic_t *i_ptr,int val);

/*2.操作 包括 獲取、增加、減少*/
/*獲取結構中的原子數值*/
int atomic_read(atomic_t *i_ptr);
/*從i_ptr 中增加指定數值inc_val */
void atomic_add(int inc_val,atomic_t *i_ptr);
/*從i_ptr 中減少指定數值dec_val */
void atomic_sub(int dec_val,atomic_t *i_ptr);
/*從i_ptr 中+1 */
void atomic_inc(atomic_t *i_ptr);
/*從i_ptr 中-1 */
void atomic_dec(atomic_t *i_ptr);

[0x412] 增加與減少後返回原子值正負結果

#include <asm/atomic.h>
/*先執行相應的操作後判斷原子變數,如果是=0則返回真,如果>0則返回假,黏連步驟還要恢復的原子變數數值*/
int atomic_sub_and_test(int dec_val,atomic_t *i_ptr);
int atomic_inc_and_test(atomic_t *i_ptr);
int atomic_dec_and_test(atomic_t *i_ptr);
/*例外的add 如果 inc_val 新增到 i_ptr後 如果原子變數 <0為真,如果原子變數 >=0為假;*/
int atomic_add_negative(int inc_val,atomic_t *i_ptr);

[0x412] 增加與減少後返回當前原子值

/*先執行相應操作後將原子變數的最終數值返回給呼叫者*/
int atomic_add_return(int inc_val,atomic_t *i_ptr);
int atomic_sub_return(int dec_val,atomic_t *i_ptr);
int atomic_inc_return(atomic_t *i_ptr);
int atomic_dec_return(atomic_t *i_ptr);