自旋鎖的使用
自旋鎖(spin_lock)是一種典型的對臨界資源進行互斥訪問的手段,顧名思義,爲了獲得一個自旋鎖,在某CPU上運行的代碼需要先執行一個原子操作,該操作測試並設置某個內存變量,在該操作完成之前其他執行單元不可能訪問到這個內存變量。
如果測試結果表明鎖已經空閒,則程序獲得這個自旋鎖並繼續執行;如果測試表明鎖仍被佔用,程序將在一個小的循環內重複這個“測試並設置”操作,就是“自旋”的動作,就是原地打轉。當自旋鎖的持有者通過重置該變量釋放這個自旋鎖後,某個等待的“測試並設置”操作向其調用者報告鎖已經釋放。
Linux中自旋鎖的操作
1. 定義自旋鎖
spinlock_t lock;
2 初始化自旋鎖
spin_lock_init(lock);
用於動態初始化自旋鎖。
3. 獲得自旋鎖
spin_lock(lock);
該宏用於獲得自旋鎖lock,如果能夠立即獲得鎖,馬上返回,否則,它將自旋在那裏直到鎖被釋放。
spin_trylock(lock);
嘗試獲得自旋鎖lock,如果能後立即獲得鎖,返回真,否則立即返回假,也就是說不會再“原地打轉”。
4. 釋放自旋鎖
spin_unlock(lock);
釋放自旋鎖lock,與lock和trylock配套使用。
5. 用法
- //declear a spin lock
- spinlock_t lock;
- spin_lock_init(&lock);
- spin_lock(&lock); //獲得自旋鎖
- ... //臨界區
- spin_unlock(&lock); //解鎖
自旋鎖主要用於SMP或者單CPU內核搶佔的情況下使用。
驅動工程師應謹慎使用,下面是需要注意的地方:
1. 自旋鎖是忙等待,在得不到鎖的時候會一直“測試並設置”,這樣的話如果長時間得不到鎖會浪費系統資源,所以適合用在等待時間比較短的情況下,不然會降低系統的性能。
2. 自旋鎖可能會導致系統死鎖。當2次試圖獲得這個自旋鎖的時候,CPU會死鎖。
3. 自旋鎖鎖定期間不能調用可能引起進程調度的函數,如果進程獲得自旋鎖之後再阻塞,如調用copy_from_user()、copy_to_user、kmalloc、msleep等函數,可能會導致內核崩潰。
下面舉例說明使用自旋鎖實現設備只能被一個進程打開。
我們還是沿用之前的globalmem的例子加以修改
- int open_count = 0; //declear open times
- spinlock_t lock;
- int globalmem_open(struct inode *inode, struct file *filp)
- {
- spin_lock(&lock);
- if(open_count) { // already open
- printk(KERN_ERR "already open!\n");
- spin_unlock(&lock);
- return -EBUSY;
- }
- open_count++;
- spin_unlock(&lock);
- printk(KERN_INFO "globalmem open!\n");
- filp->private_data = globalmem_devp;
- return 0;
- }
- int globalmem_release(struct inode *inode ,struct file *filp)
- {
- spin_lock(&lock);
- open_count--;
- spin_unlock(&lock);
- printk(KERN_INFO "globalmem release!\n");
- return 0;
- }
在init函數中初始化
- int globalmem_init(void)
- {
- int result;
- spin_lock_init(&lock);
然後我們重新編譯並添加模塊,然後使用上篇中的測試app來測試,當我們運行測試程序的時候,馬上再去打開globalmem的時候會提示設備忙。
- jay@jay:/dev$ cat globalmem
- jay@jay:/dev$ echo "123" > globalmem
- jay@jay:/dev$ cat globalmem
- cat: globalmem: Device or resource busy
- jay@jay:/dev$ echo "123" > globalmem
- bash: globalmem: Device or resource busy
- jay@jay:/dev$