在Linux中提供了一些機制用來避免競爭條件,最簡單的一個種就是自旋鎖,例如:當一個臨界區的數據在多個函數之間被調用時,為了保護數據不被破壞,可以采用spinlock來保護臨界區的數據,當然還有一個就是信號量也是可以實現臨界區數據的保護的。以後在介紹信號量吧。這裡還是先說說splinlock吧。
1:定義和初始化spinlock
在linux中定義spinlock的方法很簡單,與普通的結構體定義方式是一樣的。其代碼如下:
spinlock_t spinlock = SPIN_LOCK_UNLOCKED;
一個自旋鎖必須初始化才能被使用,可以通過在編譯階段通過宏定義來實現,比如上面的SPIN_LOCK_UNLOCKED,這個表示一個沒有鎖定的自旋鎖。同時在運行階段可以使用spin_lock_init()函數動態地初始化一個自旋鎖,其函數原型如下:
spinlock_t spin_lock_init(spinlock_t lock);
2:鎖定自旋鎖
進入臨界區之前,需要使用spin_lock宏定義來鎖定自旋鎖,spin_lock宏定義的代碼如下:
#define spin_lock(lock) _spin_lock(lock)
這個宏用來獲得lock的自旋鎖,如果能夠立即獲得自旋鎖,則宏立刻返回,否則,這個宏一直等待下去,直到被其它線程釋放為止。
3:釋放自旋鎖
退出臨界區之前,需要使用spin_unlock宏定義來釋放自旋鎖。spin_unlock宏定義的代碼如下:
#define spin_unlock(lock) _spin_unlock(lock)
這個宏用來釋放lock的自旋鎖,當調用該宏後,自旋鎖立刻被釋放。
4:使用自旋鎖
在驅動程序中,有些設備只允許打開一次,那麼就需要一個自旋鎖保護表示設備打開或者關閉的狀態的一個變量OpenCloseStatus,此處的OpenCloseStatus為一個臨界資源,如果不對OpenCloseStatus進行保護,當設備頻繁的打開時,就有可能出現錯誤的OpenCloseStatus的狀態,所以必須對OpencCloseStatus進行保護,其代碼如下:
int OpenCloseStatus;
spinlock_t spinlock;
int xxxx_init(void)
{
............
spin_lock_init(&spinlock);
............
}
int xxxx_open(struct inode *inode, struct file *filp)
{
............
spin_lock(&spinlock);
if(OpenCloseStatus)
{
spin_unlock(&spinlock);
return -EBUSY;
}
OpenCloseStatus ++;
spin_unlock(&spinlock);
...........
}
int xxxx_release(struct inode *inode, struct file *filp)
{
............
spin_lock(&spinlock);
OpenCloseStatus --;
spin_unlock(&spinlock);
...........
}
5:自旋鎖使用注意事項
1):自旋鎖一種忙等待,當條件不滿足時,會一直不斷的循環判斷條件是否滿足,如果滿足就解鎖,運行之後的代碼。因此會對linux的系統的性能有些影響。所以在實際編程時,需要注意自旋鎖不應該長時間的持有。它適合於短時間的的輕量級的加鎖機制。
2):自旋鎖不能遞歸使用,這是因為自旋鎖,在設計之初就被設計成在不同進程或者函數之間同步。所以不能用於遞歸使用。