#include
#include
#include
自旋鎖線程不能睡眠
MODULE_LICENSE("GPL");
static int __init spin_lock_init_init(void);
static void __exit spin_lock_init_exit(void);
spinlock_t lock = SPIN_LOCK_UNLOCKED;
int __init spin_lock_init_init(void)
{
printk("<0>SPIN_LOCK_UNLOCKED: %d\n",SPIN_LOCK_UNLOCKED.raw_lock.slock);
spin_lock_init( &lock ); //初始化自旋鎖
printk("<0>after init, slock: %d\n",lock.raw_lock.slock);
printk("<0>\n");
spin_lock( &lock ); //第一次獲取自旋鎖
printk("<0>first spin_lock, slock: %d\n",lock.raw_lock.slock);
spin_unlock( &lock ); //第一次釋放自旋鎖
printk("<0>first spin_unlock, slock: %d\n",lock.raw_lock.slock);
printk("<0>\n");
spin_lock( &lock ); //第二次獲取自旋鎖
printk("<0>second spin_lock, slock: %d\n",lock.raw_lock.slock);
spin_unlock( &lock ); //第二次釋放自旋鎖
printk("<0>second spin_unlock, slock: %d\n",lock.raw_lock.slock);
return 0;
}
void __exit spin_lock_init_exit(void)
{
printk("<0>exit!\n");
}
module_init(spin_lock_init_init);
module_exit(spin_lock_init_exit);
首先編譯模塊,執行命令insmod spin_lock_init.ko 插入模塊,然後執行命令dmesg –c,會出現如圖8.40所示的結果:
結果分析:
測試程序中調用了宏spin_lock( )和宏spin_unlock( ),分別用來獲取和釋放自旋鎖,關於其功能見本章中關於它們的分析。
測試程序中,首先輸出宏SPIN_LOCK_UNLOCKED的信息,它所表示的自旋鎖的狀態爲未使用。調用宏spin_lock_init( )初始化自旋鎖lock,結構體raw_spinlock_t中的slock字段被置爲0,即 Owner 和 Next 爲 0。然後第一次執行獲取和釋放鎖的操作,調用spin_lock( )後,slock字段爲256,16進製爲0x0100,即Next 域進行了加1操作;調用spin_unlock( ),slock字段爲0x0101,此時Owner域也進行了加1 操作,二者相等,鎖處於未使用狀態。第二次獲取和釋放鎖時,slock字段分別爲0x0201和0x0202,可以看到線程是按照申請順序依次獲取排隊自旋鎖的。
如果一個自旋鎖持有者在釋放鎖之前,另一個線程想要申請鎖,則它會檢測到鎖的狀態爲正在被使用(因爲鎖未釋放前Next域和Owner域不相等),從而進入忙等待。
本文不打算詳細探究spin_lock的詳細實現機制,只是最近對raw_spin_lock的出現比較困擾,搞不清楚什麼時候用spin_lock,什麼時候用raw_spin_lock,因此有了這篇文章。
/*****************************************************************************************************/
聲明:本博內容均由http://blog.csdn.net/droidphone原創,轉載請註明出處,謝謝!
/*****************************************************************************************************/
1. 臨界區(Critical Section)
- (1) 在可以搶佔(preemption)的系統中,兩個線程同時訪問同一個對象;
- (2) 線程和中斷同時訪問同一個對象;
- (3) 在多核系統中(SMP),可能兩個CPU可能同時訪問同一個對象;
2. 自旋鎖(spin_lock)
- preempt_disable();
- .....
- // 訪問共享對象的代碼
- ......
- preempt_enable();
同樣地,針對單處理器系統,第二種情況,只要臨時關閉中斷即可,我們可以使用以下方法:
- local_irq_disable();
- ......
- // 訪問共享對象的代碼
- ......
- local_irq_enable();
那麼,針對多處理器的系統,以上的方法還成立麼?答案很顯然:不成立。
對於第一種情況,雖然搶佔被禁止了,可是另一個CPU上還有線程在運行,如果這個線程也正好要訪問該共享對象,上面的代碼段顯然是無能爲力了。
對於第二種情況,雖然本地CPU的中斷被禁止了,可是另一個CPU依然可以產生中斷,如果他的中斷服務程序也正好要訪問該共享對象,上面的代碼段也一樣無法對共享對象進行保護。
- spin_lock(spinlock_t *lock);
- spin_unlock(spinlock_t *lock);
- spin_lock_irq(spinlock_t *lock);
- spin_unlock_irq(spinlock_t *lock);
- spin_lock_irqsave(lock, flags);
- spin_lock_irqrestore(lock, flags);
- 如果只是在普通線程之間同時訪問共享對象,使用spin_lock()/spin_unlock();
- 如果是在中斷和普通線程之間同時訪問共享對象,並且確信退出臨界區後要打開中斷,使用spin_lock_irq()/spin_unlock_irq();
- 如果是在中斷和普通線程之間同時訪問共享對象,並且退出臨界區後要保持中斷的狀態,使用spin_lock_irqsave()/spin_unlock_irqrestore();
3. raw_spin_lock
- 把原來的raw_spin_lock改爲arch_spin_lock;
- 把原來的spin_lock改爲raw_spin_lock;
- 實現一個新的spin_lock;
- 儘可能使用spin_lock;
- 絕對不允許被搶佔和休眠的地方,使用raw_spin_lock,否則使用spin_lock;
- 如果你的臨界區足夠小,使用raw_spin_lock;
總結:
1、自旋鎖不能休眠
2、自旋鎖互斥的過程:
spi_lock(); 關閉調度器的課搶佔性, 讀自旋鎖,檢測,跟新自旋鎖 (定義的是一個全局變量鎖)
spi_lock_irq; 關閉中斷(local_irq_disable),關閉調度器的課搶佔性, 讀自旋鎖,檢測,跟新自旋鎖
3、併發的概念,主要是對共享資源的競爭狀態,不一定是時間上的。