【嵌入式Linux驅動開發】十一、Linux併發與競爭 - 原子操作、自旋鎖、信號量、互斥體

一、原子操作

原子整形操作API
函數 描述
ATOMIC_INIT(int i) 定義原子變量的時候對其初始化。
int atomic_read(atomic_t *v) 讀取 v 的值,並且返回。
void atomic_set(atomic_t *v, int i) 向 v 寫入 i 值。
void atomic_add(int i, atomic_t *v) 給 v 加上 i 值。
void atomic_sub(int i, atomic_t *v) 從 v 減去 i 值。
void atomic_inc(atomic_t *v) 給 v 加 1,也就是自增。
void atomic_dec(atomic_t *v) 從 v 減 1,也就是自減
int atomic_dec_return(atomic_t *v) 從 v 減 1,並且返回 v 的值。
int atomic_inc_return(atomic_t *v) 給 v 加 1,並且返回 v 的值。
int atomic_sub_and_test(int i, atomic_t *v) 從 v 減 i,如果結果爲 0 就返回真,否則返回假
int atomic_dec_and_test(atomic_t *v) 從 v 減 1,如果結果爲 0 就返回真,否則返回假
int atomic_inc_and_test(atomic_t *v) 給 v 加 1,如果結果爲 0 就返回真,否則返回假
int atomic_add_negative(int i, atomic_t *v) 給 v 加 i,如果結果爲負就返回真,否則返回假
原子位操作API
函數 描述
void set_bit(int nr, void *p) 將 p 地址的第 nr 位置 1。
void clear_bit(int nr,void *p) 將 p 地址的第 nr 位清零。
void change_bit(int nr, void *p) 將 p 地址的第 nr 位進行翻轉。
int test_bit(int nr, void *p) 獲取 p 地址的第 nr 位的值。
int test_and_set_bit(int nr, void *p) 將 p 地址的第 nr 位置 1,並且返回 nr 位原來的值。
int test_and_clear_bit(int nr, void *p) 將 p 地址的第 nr 位清零,並且返回 nr 位原來的值。
int test_and_change_bit(int nr, void *p) 將 p 地址的第 nr 位翻轉,並且返回 nr 位原來的

二、自旋鎖

使用注意如下:

  • ①、因爲在等待自旋鎖的時候處於“自旋”狀態,因此鎖的持有時間不能太長,一定要短,否則的話會降低系統性能。如果臨界區比較大,運行時間比較長的話要選擇其他的併發處理方式,比如稍後要講的信號量和互斥體。
  • ②、自旋鎖保護的臨界區內不能調用任何可能導致線程休眠的 API 函數,否則的話可能導致死鎖。
  • ③、不能遞歸申請自旋鎖,因爲一旦通過遞歸的方式申請一個你正在持有的鎖,那麼你就必須“自旋”,等待鎖被釋放,然而你正處於“自旋”狀態,根本沒法釋放鎖。結果就是自己把自己鎖死了!
  • ④、在編寫驅動程序的時候我們必須考慮到驅動的可移植性,因此不管你用的是單核的還是多核的 SOC,都將其當做多核 SOC 來編寫驅動程序。
基本自旋鎖API
函數 描述
DEFINE_SPINLOCK(spinlock_t lock) 定義並初始化一個自選變量。
int spin_lock_init(spinlock_t *lock) 初始化自旋鎖。
void spin_lock(spinlock_t *lock) 獲取指定的自旋鎖,也叫做加鎖。
void spin_unlock(spinlock_t *lock) 釋放指定的自旋鎖。
int spin_trylock(spinlock_t *lock) 嘗試獲取指定的自旋鎖,如果沒有獲取到就返回 0
int spin_is_locked(spinlock_t *lock) 檢查指定的自旋鎖是否被獲取,如果沒有被獲取就返回非 0,否則返回 0。
中斷自旋鎖API
函數 描述
void spin_lock_irq(spinlock_t *lock) 禁止本地中斷,並獲取自旋鎖。
void spin_unlock_irq(spinlock_t *lock) 激活本地中斷,並釋放自旋鎖。
void spin_lock_irqsave(spinlock_t *lock,unsigned long flags) 保存中斷狀態,禁止本地中斷,並獲取自旋鎖。
void spin_unlock_irqrestore(spinlock_t*lock, unsigned long flags) 將中斷狀態恢復到以前的狀態,並且激活本地中斷,釋放自旋鎖。
下半部自旋鎖API
函數 描述
void spin_lock_bh(spinlock_t *lock) 關閉下半部,並獲取自旋鎖。
void spin_unlock_bh(spinlock_t *lock) 打開下半部,並釋放自旋鎖。

三、信號量

使用信號量會提高處理器的使用效率,畢竟不用一直傻乎乎的在那裏“自旋”等待。但是,信號量的開銷要比自旋鎖大,因爲信號量使線程進入休眠狀態以後會切換線程,切換線程就會有開銷。

使用注意如下:

  • ①、因爲信號量可以使等待資源線程進入休眠狀態,因此適用於那些佔用資源比較久的場合。
  • ②、因此信號量不能用於中斷中,因爲信號量會引起休眠,中斷不能休眠。
  • ③、如果共享資源的持有時間比較短,那就不適合使用信號量了,因爲頻繁的休眠、切換線程引起的開銷要遠大於信號量帶來的那點優勢。
信號量API
函數 描述
DEFINE_SEAMPHORE(name) 定義一個信號量,並且設置信號量的值爲 1。
void sema_init(struct semaphore *sem, int val) 初始化信號量 sem,設置信號量值爲 val。
void down(struct semaphore *sem) 獲取信號量,因爲會導致休眠,因此不能在中斷中使用。
int down_trylock(struct semaphore *sem); 嘗試獲取信號量,如果能獲取到信號量就獲取,並且返回 0。如果不能就返回非 0,並且不會進入休眠。
int down_interruptible(struct semaphore *sem) 獲取信號量,和 down 類似,只是使用 down 進入休眠狀態的線程不能被信號打斷。而使用此函數進入休眠以後是可以被信號打斷的。
void up(struct semaphore *sem) 釋放信號量

四、互斥體

使用注意如下:

  • ①、 mutex 可以導致休眠,因此不能在中斷中使用 mutex,中斷中只能使用自旋鎖。
  • ②、和信號量一樣, mutex 保護的臨界區可以調用引起阻塞的 API 函數。
  • ③、因爲一次只有一個線程可以持有 mutex,因此,必須由 mutex 的持有者釋放 mutex。並且 mutex 不能遞歸上鎖和解鎖
互斥體API
函數 描述
DEFINE_MUTEX(name) 定義並初始化一個 mutex 變量。
void mutex_init(mutex *lock) 初始化 mutex。
void mutex_lock(struct mutex *lock) 獲取 mutex,也就是給 mutex 上鎖。如果獲取不到就進休眠。
void mutex_unlock(struct mutex *lock) 釋放 mutex,也就給 mutex 解鎖。
int mutex_trylock(struct mutex *lock) 嘗試獲取 mutex,如果成功就返回 1,如果失敗就返回 0。
int mutex_is_locked(struct mutex *lock) 判斷 mutex 是否被獲取,如果是的話就返回1,否則返回 0。
int mutex_lock_interruptible(struct mutex *lock) 使用此函數獲取信號量失敗進入休眠以後可以被信號打斷。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章