分析Linux中Spinlock在ARM及X86平台上的实现
时间:2017-01-03作者:华清远见
本文主要以2.6.22.6内核分析Linux中spinlock在ARM及X86平台上的实现(不同版本的内核实现形式会有一些差异,但原理大致相同)。此处默认大家已经熟悉了spinlock的使用,重点解释容易引起迷惑的体系结构相关的实现部分。 一、spin_lock(lock)的实现 /***include/linux/spinlock.h中***/
#if defined(CONFIG_SMP) || defined(CONFIG_DEBUG_SPINLOCK) 1、如果是单处理器 /****include/linux/spinlock_api_up.h****/
#define _spin_lock(lock) __LOCK(lock)
(1)preempt_disable():禁止抢占 这是一对用于sparse对代码检测的相互关联的函数定义,第一句表示要增加变量x的计数,增加量为1,第二句则正好相反,这个是用来函数编译的过程中。如果在代码中出现了不平衡的状况,那么在Sparse的检测中就会报警。如果要使用Sparse检测功能就需要安装sparse工具(参考相关安装方法),然后编译内核
#make zImage C=1 (C=1,只检测新编译的文件,C=2是查所有文件) (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) /***include/linux/spinlock.h***/
#ifdef CONFIG_DEBUG_SPINLOCK 3、__raw_spin_lock在ARM处理器上的实现 /******include/asm-arm/spinlock_types.h***/
typedef struct { #define __RAW_SPIN_LOCK_UNLOCKED { 0 } /******include/asm-arm/spinlock.h***/
#if __LINUX_ARM_ARCH__ < 6 上述代码关键在于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 { /******include/asm-i386/spinlock.h***/
static inline void __raw_spin_lock(raw_spinlock_t *lock) 在多处理器环境中 LOCK_PREFIX 实际被定义为 “lock”前缀。x86 处理器使用“lock”前缀的方式提供了在指令执行期间对总线加锁的手段。芯片上有一条引线 LOCK,如果在一条汇编指令(ADD, ADC, AND, BTC, BTR, BTS, CMPXCHG, CMPXCH8B, DEC, INC, NEG, NOT, OR, SBB, SUB, XOR, XADD, XCHG)前加上“lock” 前缀,经过汇编后的机器代码就使得处理器执行该指令时把引线 LOCK 的电位拉低,从而把总线锁住,这样其它处理器或使用DMA的外设暂时无法通过同一总线访问内存。 jns 汇编指令检查 EFLAGS 寄存器的 SF(符号)位,如果为 0,说明 slock 原来的值为 1,则线程获得锁,然后跳到标签 3 的位置结束本次函数调用。如果 SF 位为 1,说明 slock 原来的值为 0 或负数,锁已被占用。那么线程转到标签 2 处不断测试 slock 与 0 的大小关系,假如 slock 小于或等于 0,跳转到标签 2 的位置继续忙等待;假如 slock 大于 0,说明锁已被释放,则跳转到标签 1 的位置重新申请锁。 二、spin_unlock(lock)的实现 /***include/linux/spinlock.h***/
#if defined(CONFIG_DEBUG_SPINLOCK) || defined(CONFIG_PREEMPT) || \ 1、 如果是单处理器 /****include/linux/spinlock_api_up.h****/
#define _spin_unlock(lock) __UNLOCK(lock) 完成前文的获取锁的逆过程 2、如果配置了SMP
# define spin_unlock(lock) \ 3、__raw_spin_unlock在ARM处理器上的实现 /******include/asm-arm/spinlock.h***/
static inline void __raw_spin_unlock(raw_spinlock_t *lock) __raw_spin_unlock只是简单的给lock->lock里写0。 4、__raw_spin_unlock在X86处理器上的实现 /***include/asm-i386/spinlock.h***/
static inline void __raw_spin_unlock(raw_spinlock_t *lock) __raw_spin_unlock 函数仅仅执行一条汇编指令:将lock-> slock 置为 1。
相关资讯
发表评论
|