zoukankan      html  css  js  c++  java
  • spin_lock浅析【转】

    转自: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] "
    "    teq    %0, #0 "
    "    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嘛,呵呵,解锁完成。

    至此完成自旋锁的初步分析,基本脉络清楚了,代码中还有部分不了解含义,以后再研究,到此休息一下。

  • 相关阅读:
    第四次作业—四则运算
    第四次作业—代码规范
    【欢迎来怼】事后诸葛亮会议
    软件工程——第七次作业
    第17次Scrum会议(10/29)【欢迎来怼】
    软件工程——第六次作业
    第10次Scrum会议(10/22)【欢迎来怼】
    软件工程——第五次作业
    欢迎来怼——第四次Scrum会议
    软件工程——第四次作业(3)
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/7121582.html
Copyright © 2011-2022 走看看