微波EDA网,见证研发工程师的成长!
首页 > 硬件设计 > 嵌入式设计 > 分析Linux中Spinlock在ARM及X86平台上的实现

分析Linux中Spinlock在ARM及X86平台上的实现

时间:12-08 来源:互联网 点击:

  本文主要以2.6.22.6内核分析Linux中spinlock在ARM及X86平台上的实现(不同版本的内核实现形式会有一些差异,但原理大致相同)。此处默认大家已经熟悉了spinlock的使用,重点解释容易引起迷惑的体系结构相关的实现部分。

  一、spin_lock(lock)的实现

  /***include/linux/spinlock.h中***/

  #if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK)

  //如果配置了SMP或配置自旋锁调试功能

  # include linux/spinlock_api_smp.h>

  #else //如果是单处理器且不配置自旋锁调试功能

  # include linux/spinlock_api_up.h>

  #endif

  ……

  #define spin_lock(lock) _spin_lock(lock)

  1、如果是单处理器

  /****include/linux/spinlock_api_up.h****/

  #define _spin_lock(lock) __LOCK(lock)

  #define __LOCK(lock) \

  do { preempt_disable(); __acquire(lock); (void)(lock); } while (0)

  (1)preempt_disable():禁止抢占

  (2)__acquire(lock):在include/linux/compiler.h中有定义

  #ifdef __CHECKER__

  ……

  # define __acquire(x) __context__(x,1)

  # define __release(x) __context__(x,-1)

  #else

  ……

  # define __acquires(x)

  # define __releases(x)

  这是一对用于sparse对代码检测的相互关联的函数定义,第一句表示要增加变量x的计数,增加量为1,第二句则正好相反,这个是用来函数编译的过程中。如果在代码中出现了不平衡的状况,那么在Sparse的检测中就会报警。如果要使用Sparse检测功能就需要安装sparse工具(参考相关安装方法),然后编译内核

  #make zImage C=1 (C=1,只检测新编译的文件,C=2是查所有文件)

  Sparse会定义__CHECKER__,如果你没有使用sparse工具,__acquire(lock)则定义为空

  (3)(void)(lock):通过插入一个变量本身的求值表达式,使编译器不再报警,如:“variable 'lock' is defined but never used”。这种求值不会影响运行时的速度。

  2、如果配置了SMP

  /****include/linux/spinlock_api_smp.h中****/

  void __lockfunc _spin_lock(spinlock_t *lock) __acquires(lock);

  /***kernel/spinlock.c***/

  void __lockfunc _spin_lock(spinlock_t *lock)

  {

  preempt_disable();

  //关闭抢占

  spin_acquire(lock->dep_map, 0, 0, _RET_IP_);

  //自旋锁调试用,在没有定义自旋锁调试的时候是空函数

  _raw_spin_lock(lock);

  }

  /***include/linux/spinlock.h***/

  #ifdef CONFIG_DEBUG_SPINLOCK

  extern void _raw_spin_lock(spinlock_t *lock);//在lib/spinlock_debug.c中实现

  #else //smp情况

  # define _raw_spin_lock(lock) __raw_spin_lock((lock)->raw_lock)

  3、__raw_spin_lock在ARM处理器上的实现

  /******include/asm-arm/spinlock_types.h***/

  typedef struct {

  volatile unsigned int lock;

  } raw_spinlock_t;

  #define __RAW_SPIN_LOCK_UNLOCKED { 0 }

  /******include/asm-arm/spinlock.h***/

  #if __LINUX_ARM_ARCH__ 6

  #error SMP not supported on pre-ARMv6 CPUs //ARMv6后,才有多核ARM处理器

  #endif

  ……

  static inline void __raw_spin_lock(raw_spinlock_t *lock)

  {

  unsigned long tmp;

  __asm__ __volatile__(

  "1: ldrex %0, [%1]"

  //取lock->lock放在 tmp里,并且设置lock->lock这个内存地址为独占访问

  " teq %0, #0"

  //测试lock_lock是否为0,影响标志位z

  #ifdef CONFIG_CPU_32v6K

  " wfene"

  #endif

  " strexeq %0, %2, [%1]"

  //如果lock_lock是0,并且是独占访问这个内存,就向lock->lock里写入1,并向tmp返回0,同时清除独占标记

  " teqeq %0, #0"

  //如果lock_lock是0,并且strexeq返回了0,表示加锁成功,返回

  " bne 1b"

  //如果上面的条件(1:lock->lock里不为0,2:strexeq失败)有一个符合,就在原地打转

  : "=r" (tmp) //%0:输出放在tmp里,可以是任意寄存器

  : "r" (lock->lock), "r" (1)

  //%1:取lock->lock放在任意寄存器,%2:任意寄存器放入1

  : "cc"); //状态寄存器可能会改变

  smp_mb();

  }

  上述代码关键在于LDREX和STREX指令的应用。DREX和STREX指令是在V6以后才出现的,代替了V6以前的swp指令。可以让bus监控LDREX和STREX指令之间有无其它CPU和DMA来存取过这个地址,若有的话STREX指令的第一个寄存器里设置为1(动作失败),若没有,指令的第一个寄存器里设置为0(动作成功)。

  不仅是自旋锁用到LDREX和STREX指令,信号量的实现也是利用LDREX和STREX指令来实现的。

  4、__raw_spin_lock在X86处理器上的实现

  /******include/asm-i386/spinlock_types.h***/

  typedef struct {

  unsigned int slock;

} raw

Copyright © 2017-2020 微波EDA网 版权所有

网站地图

Top