spin_lock淺析

來源:互聯網
上載者:User

 

今天我們詳細瞭解一下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嘛,呵呵,解鎖完成。

 

至此完成自旋鎖的初步分析,基本脈絡清楚了,代碼中還有部分不瞭解含義,以後再研究,到此休息一下。

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.