轉自:http://blog.csdn.net/frankyzhangc/article/details/6569475
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
今天我們詳細了解一下spin_lock在內核中代碼實現,我們總共分析四個項目:
1.spinlock_t的定義分析:
首先來看一下spinlock_t的定義:
typedef struct {
raw_spinlock_t raw_lock;
#if defined(CONFIG_PREEMPT) &&defined(CONFIG_SMP)
unsigned int break_lock;
#endif
#ifdef CONFIG_DEBUG_SPINLOCK
unsigned int magic, owner_cpu;
void *owner;
#endif
} spinlock_t;
從上面代碼來分析一個完整的spinlock_t的結構有5個成員:raw_lock/ break_lock/ magic/ owner_cpu/ owner,但是這5個成員都沒有初始值,所以顯然要一個函數去初始化它們。
2. spin_lock_init函數分析
我們通常用spinlock_tlock來定義一把自旋鎖,然后要初始化自旋鎖通過函數spin_lock_init(&lock);這個函數的定義為
/**********************************************************************/
#define spin_lock_init(lock) do { *(lock) = SPIN_LOCK_UNLOCKED; } while(0)
/**********************************************************************/
從代碼分析,所謂初始化就是把一個unlock宏的值賦給lock,從字面的意思來看就是把鎖初始化為解鎖,那么我們再追蹤這個宏的定義:
# defineSPIN_LOCK_UNLOCKED /
(spinlock_t) { .raw_lock= __RAW_SPIN_LOCK_UNLOCKED, /
.magic= SPINLOCK_MAGIC, /
.owner= SPINLOCK_OWNER_INIT, /
.owner_cpu= -1 }
這樣就很清晰了,初始化的目的就是把spinlock_t中的各個成員賦了初始值。
第一個成員raw_lock被賦予了__RAW_SPIN_LOCK_UNLOCKED是什么意思呢,繼續追蹤,這個宏在include/linuxspinlock_types_up.h中定義的為:
#define __RAW_SPIN_LOCK_UNLOCKED { 1 } 所以鎖初始化時是把成員raw_lock賦值為1,即解鎖狀態。其他的初始值的含義我尚不了解
3. 加鎖宏spin_lock(lock)宏的分析:
宏的定義如下:
#define spin_lock(lock) _spin_lock(lock)
繼續追蹤其中的函數_spin_lock(lock)定義如下:
void __lockfunc _spin_lock(spinlock_t*lock)
{
preempt_disable();
_raw_spin_lock(lock);
}
這個函數核心就是_raw_spin_lock函數,柯南繼續追蹤:
void _raw_spin_lock(spinlock_t *lock)
{
debug_spin_lock_before(lock);
if(unlikely(!__raw_spin_trylock(&lock->raw_lock)))
__spin_lock_debug(lock);
debug_spin_lock_after(lock);
}
Debug的不管,那么核心函數就是__spin_lock_debug(lock),快去看看它的定義吧:
static void __spin_lock_debug(spinlock_t*lock)
{
intprint_once = 1;
u64i;
for(;;) {
for(i = 0; i < loops_per_jiffy * HZ; i++) {
cpu_relax();//空函數,不知道用意是什么
if(__raw_spin_trylock(&lock->raw_lock))
return;
}
/*lockup suspected: */
if(print_once) {
print_once= 0;
printk("BUG:spinlock lockup on CPU#%d, %s/%d, %p/n",
smp_processor_id(),current->comm, current->pid,
lock);
dump_stack();
}
}
}
哈哈,看到for (;;)就知道死循環了,那么自旋鎖很明顯時不會讓出CPU的,除非它能夠加鎖成功,否則就一直自旋吧!其中HZ是CPU相關的,一個CPU時鍾每秒中跳動HZ次,這個值就是一個jiffes值。對於ARM來說1秒跳動100次,HZ為100,對於X86/PPC: 1000。loops_per_jiffy= (1<<12),轉化十進制為4096。兩個相乘的含義就是每個jiffes中進行4096次循環,而一秒鍾里面用HZ個jiffes,兩個值相乘得到的就是1秒鍾進行循環的次數。從這個for循環來看,就是在1秒內不斷循環進行__raw_spin_trylock動作(),如果成功就跳出死循環,如果不成功就繼續循環了,這樣就完成了自旋的功能,而且進程不會睡眠。
static inline int __raw_spin_trylock(raw_spinlock_t *lock)
{
unsigned long tmp;
__asm__ __volatile__(
" ldrex %0, [%1]\n"
" teq %0, #0\n"
" strexeq %0, %2, [%1]"
: "=&r" (tmp)
: "r" (&lock->lock), "r" (1)
: "cc");
if (tmp == 0) {
smp_mb();
return 1;
} else {
return 0;
}
}
ARM指令LDREX和STREX可以保證在兩條指令之間進行的取值-測試操作的原子性,假設有進程A和B都試圖調用上述函數獲得寫入鎖,那么誰先執行LDREX指令誰將先獲得鎖。 首先這段匯編是AT&T內聯匯編,首先%0代表=&r" (tmp),%1代表 "r" (&lock->lock),%2代表 "r" (1),以此類推,這樣的話首先從lock中把值取出來,放到tmp里面,然后用測試指令比較tmp是否為0,如果是0代表代表沒有人獲得鎖。然后使用strex指令把lock的值設為1,獲取鎖。如果lock的值不為0,表明之前該鎖已經被被其他的進程所使用,那么該進程將自旋。
4. 加鎖宏spin_lock(lock)宏的分析:
#define spin_unlock(lock) _spin_unlock(lock)
無需多言
void __lockfunc _spin_unlock(spinlock_t*lock)
{
_raw_spin_unlock(lock);
preempt_enable();
}
這個函數中先調用_raw_spin_unlock,然后preempt_enable(什么含義,fixme)。
void _raw_spin_unlock(spinlock_t *lock)
{
debug_spin_unlock(lock);
__raw_spin_unlock(&lock->raw_lock);
}
繼續追蹤__raw_spin_unlock(&lock->raw_lock)內核真的很能繞彎子,大家慢慢習慣了
static inline void __raw_spin_unlock(raw_spinlock_t *lock)
{
smp_mb();
__asm__ __volatile__(
" str %1, [%0]"
:
: "r" (&lock->lock), "r" (0)
: "cc");
}
不就是給lock成員賦值0嘛,呵呵,解鎖完成。
至此完成自旋鎖的初步分析,基本脈絡清楚了,代碼中還有部分不了解含義,以后再研究,到此休息一下。