收藏 分享(赏)

LINUX内核之普通自旋锁.doc

上传人:dzzj200808 文档编号:2649288 上传时间:2018-09-24 格式:DOC 页数:14 大小:325KB
下载 相关 举报
LINUX内核之普通自旋锁.doc_第1页
第1页 / 共14页
LINUX内核之普通自旋锁.doc_第2页
第2页 / 共14页
LINUX内核之普通自旋锁.doc_第3页
第3页 / 共14页
LINUX内核之普通自旋锁.doc_第4页
第4页 / 共14页
LINUX内核之普通自旋锁.doc_第5页
第5页 / 共14页
点击查看更多>>
资源描述

1、普通自旋锁自旋锁最常见的使用场景是创建一段临界区 :static DEFINE_SPINLOCK(xxx_lock);unsigned long flags;spin_lock_irqsave(. critical section here spin_unlock_irqrestore(自旋锁使用时值得注意的是:对于采用使用自旋锁以保证共享变量的存取安全时,仅当系统中所有涉及 到存取该共享变量的程序部分都采用 成对的 spin_lock、和 spin_unlock 来进行操作才能保证其安全性。NOTE! The spin-lock is safe only when you _also_ us

2、e the lock itself to do locking across CPUs, which implies that EVERYTHING that touches a shared variable has to agree about the spinlock they want to use.在 Linux2.6.15.5 中,自旋体数据结构如下:当配置 CONFIG_SMP 时,raw_spinlock_t 才是一个含有 slock 变量的结构,该 slock 字段标识自旋锁是否空闲状态,用以处理多 CPU 处理器并发申请锁的情况;当未配置CONFIG_SMP 时,对于单 C

3、PU 而言,http:/ 不会发生发申请自旋锁,故raw_lock 为空结构体。当配置 CONFIG_SMP 和 CONFIG_PREEMPT 时,spinlock_t 才会有 break_lock 字段,break_lock 字段用于标记自旋锁竞争状态,当 break_lock = 0 时表示没有多于两个的执行路径,当 break_lock = 1 时表示没有其它进程在忙等待该锁。当在 SMP 多 CPU 体系架构下有可能出现申请不到自旋锁、空等的情况,但 LINUX 内核必须保证在 spin_lock 的原子性,故在配置 CONFIG_PREEMPT 时必须禁止内核抢占。字段 描述spin

4、_lock_init(lock) 一个自旋锁时,可使用接口函数将其初始化为锁定状态spin_lock(lock) 用于锁定自旋锁,如果成功则返回;否则循环等待自旋锁变为空闲spin_unlock(lock) 释放自旋锁 lock,重新设置自旋锁为锁定状态spin_is_locked(lock) 判断当前自旋锁是否处于锁定状态spin_unlock_wait(lock) 循环等待、直到自旋锁 lock 变为可用状态spin_trylock(lock) 尝试锁定自旋锁 lock,如不成功则返回 0;否则锁定,并返回 1 http:/ spin_can_lock(lock) 判断自旋锁 lock 是

5、否处于空闲状态spin_lock 和 spin_unlock 的关系如下:可见,在 UP 体系架构 中,由于没有必要有实际的锁以防止多 CPU 抢占,spin 操作仅仅是禁止和开启内核抢占。LINUX 2.6.35 版本,将 spin lock 实现更改为 ticket lock。spin_lock 数据结构除了用于内核调试之外,字段为:raw_spinlock rlock。ticket spinlock 将 rlock 字段分解为如下两部分:Next 是下一个票号,而 Owner 是允许使用自旋锁的票号。加锁时 CPU 取 Next,并将rlock.Next + 1。将 Next 与 Own

6、er 相比较,若相同,则加锁成功;否则循环等待、直到Next = rlock.Owner 为止。解锁则直接将 Owner + 1 即可。spin_lock 和 spin_unlock 的调用关系如下:普通自旋锁源码分析源程序文件目录关系图在/include/linux/spinlock.h 中通过是否配置 CONFIG_SMP 项判断导入哪种自旋锁定义及操作:004 /*005 * include/linux/spinlock.h - generic spinlock/rwlock declarations007 * heres the role of the various spinlock

7、/rwlock related include files:009 * on SMP builds:011 * asm/spinlock_types.h: contains the arch_spinlock_t/arch_rwlock_t and the012 * initializers014 * linux/spinlock_types.h:015 * defines the generic type and initializers017 * asm/spinlock.h: contains the arch_spin_*()/etc. lowlevel018 * implementa

8、tions, mostly inline assembly code022 * linux/spinlock_api_smp.h:023 * contains the prototypes for the _spin_*() APIs.025 * linux/spinlock.h: builds the final spin_*() APIs.027 * on UP builds:029 * linux/spinlock_type_up.h:030 * contains the generic, simplified UP spinlock type.031 * (which is an em

9、pty structure on non-debug builds)033 * linux/spinlock_types.h:034 * defines the generic type and initializers036 * linux/spinlock_up.h:037 * contains the arch_spin_*()/etc. version of UP038 * builds. (which are NOPs on non-debug, non-preempt039 * builds)041 * (included on UP-non-debug builds:)043 *

10、 linux/spinlock_api_up.h:044 * builds the _spin_*() APIs.046 * linux/spinlock.h: builds the final spin_*() APIs.047 */082 /*083 * Pull the arch_spin*() functions/declarations (UP-nondebug doesnt need them):084 */085 #ifdef CONFIG_SMP086 # include 087 #else088 # include 089 #endif064 typedef struct s

11、pinlock 065 union 066 struct raw_spinlock rlock;075 ;076 spinlock_t;282 static inline void spin_lock(spinlock_t *lock)283 284 raw_spin_lock(285 169 #define raw_spin_lock(lock) _raw_spin_lock(lock)322 static inline void spin_unlock(spinlock_t *lock)323 324 raw_spin_unlock(325 222 #define raw_spin_unl

12、ock(lock) _raw_spin_unlock(lock)UP 体系架构spin_lock 函数在 UP 体系架构中最终实现方式为:/include/linux/spinlock_api_up.h052 #define _raw_spin_lock(lock) _LOCK(lock)021 /*022 * In the UP-nondebug case theres no real locking going on, so the023 * only thing we have to do is to keep the preempt counts and irq024 * flags

13、straight, to suppress compiler warnings of unused lock025 * variables, and to add the proper checker annotations:026 */027 #define _LOCK(lock) 028 do preempt_disable(); _acquire(lock); (void)(lock); while (0)052 #define _raw_spin_lock(lock) _LOCK(lock)preempt_disable 在未配置 CONFIG_PREEMPT 时为空函数,否则禁止内核

14、抢占。而_acquire()用于内核编译过程中静态检查。(void)(lock)则是为避免编译器产生 lock 未被使用的警告。spin_unlock 函数在 UP 体系架构中最终实现方式为:039 #define _UNLOCK(lock) 040 do preempt_enable(); _release(lock); (void)(lock); while (0)SMP 体系架构 -Tickect Spin Lock 的实现方式在 Linux2.6.24 中,自旋锁由一个整数表示,当为 1 时表示锁是空闲的, spin_lock()每次减少 1,故 dep_map, 0, 0, _RET

15、_IP_);144 LOCK_CONTENDED(lock, do_raw_spin_trylock, do_raw_spin_lock);145 在_raw_spin_lock 中,首先禁止内核抢占,调用 LOCK_CONTENED 宏391 #define LOCK_CONTENDED(_lock, try, lock) 392 do 393 if (!try(_lock) 394 lock_contended( 395 lock(_lock); 396 397 lock_acquired( 398 while (0)其中即在_raw_spin_lock 中,即为首先调用 do_raw_s

16、pin_trylock 尝试加锁,若失败则继续调用 do_raw_spin_lock 进行加锁。而 do_raw_spin_xxx 具体实现与平台有关。/include/linux/spinlock.h136 static inline void do_raw_spin_lock(raw_spinlock_t *lock) _acquires(lock)137 138 _acquire(lock);139 arch_spin_lock(140 149 static inline int do_raw_spin_trylock(raw_spinlock_t *lock)150 151 retur

17、n arch_spin_trylock(152 在 X86 平台下,do_raw_spin_lock 和 do_raw_spin_trylock 实现为两个函数:/arch/x86/include/asm/spinlock.h188 static _always_inline void arch_spin_lock(arch_spinlock_t *lock)189 190 _ticket_spin_lock(lock);191 192 193 static _always_inline int arch_spin_trylock(arch_spinlock_t *lock)194 195 r

18、eturn _ticket_spin_trylock(lock);196 058 #if (NR_CPUS slock)076 :077 : “memory“, “cc“);078 066 行 :LOCK_PREFIX 在 UP 上为空定义,而在 SMP 上为 Lock,用以保证从 066 行074 行 为原子操作,强制所有 CPU 缓存失效。xaddw 指令用法如下:xaddw src, dsc =tmp = dscdesc = dsc + srcsrc = tmpXADDW 语法验证实验:xaddw 使%0 和%1 按 1 个 word 长度交换相加,即:%0: inc slock, %1

19、: slock slock + 0x0100。%1 此时高字节 Next + 1。xaddw 使%0 和%1 内容改变如下:068 行 :比较 inc 中自己的 Next 是否与 Owner 中 ticket 相等,若相等则获取自旋锁使用权、结束循环。070 行 073 行 :如果 Owner 不属于自己,则执行空语句,并重新读取 slock 中的Owner,跳回至 068 行进行判断。为什么要用 LOCK_PREFIX 宏来代替直接使用 lock 指令的方式呢?解释如下:为了避免在配置了 CONFIG_SMP 项编译产生的 SMP 内核、实际却运行在 UP 系统上时系统执行 lock 命令所

20、带来的开销,系统创建在.smp_locks 一张 SMP alternatives table 用以保存系统中所有lock 指令的指针。当实际运行时,若从 SMPUP 时,可以根据 .smp_locks lock 指针表通过热补丁的方式将 lock 指令替换成 nop 指令。当然也可以实现系统运行时将锁由 UPSMP的切换。具体应用可参见参考资料Linux 内核 LOCK_PREFIX 的含义。009 /*010 * Alternative inline assembly for SMP.011 *012 * The LOCK_PREFIX macro defined here replace

21、s the LOCK and013 * LOCK_PREFIX macros used everywhere in the source tree.014 *015 * SMP alternatives use the same data structures as the other016 * alternatives and the X86_FEATURE_UP flag to indicate the case of a017 * UP system running a SMP kernel. The existing apply_alternatives()018 * works fi

22、ne for patching a SMP kernel for UP.019 *020 * The SMP alternative tables can be kept after boot and contain both021 * UP and SMP versions of the instructions to allow switching back to022 * SMP at runtime, when hotplugging in a new CPU, which is especially023 * useful in virtualized environments.02

23、4 *025 * The very common lock prefix is handled as special case in a026 * separate table which is a pure address list without replacement ptr027 * and size information. That keeps the table sizes small.028 */029 030 #ifdef CONFIG_SMP031 #define LOCK_PREFIX_HERE 032 “.section .smp_locks,“a“n“ 033 “.b

24、align 4n“ 034 “.long 671f - .n“ /* offset */ 035 “.previousn“ 036 “671:“037 038 #define LOCK_PREFIX LOCK_PREFIX_HERE “ntlock; “039 040 #else /* ! CONFIG_SMP */041 #define LOCK_PREFIX_HERE “042 #define LOCK_PREFIX “043 #endif032 行 “.section .smp_locks, a”,表示以下代码生成在.smp_locks 段中,而“a” 代表allocatable。033

25、 行 034 行 “.balign 4 .long 571f”,表示以 4 字节对齐、将 671 标签的地址置于.smp_locks 段中,而标签 671 的地址即为:代码段 lock 指令的地址。(其实就是 lock 指令的指针啦)033 行 034 行 “.previous”伪指令,表示恢复以前 section,即代码段。故在 038 行 将导致在代码段生成 lock 指令。LOCK_CONTENDED 时首先尝试使用_ticket_spin_trylock 对 lock 进行加锁,若失败则继续使用_ticket_spin_lock 进行加锁。不直接调用_ticket_spin_lock

26、而使用_ticket_spin_trylock 的原因是:trylock 首先不会修改 lock.slock 的 ticket,它只是通过再次检查, 1)将 slock 读出,并判断 slock 是否处于空闲状态;2)调用 LOCK 执行原子操作,判断当前 slock 的 Next 是否已经被其它 CPU 修改,若未被修改则获得该锁,并将 lock.slock.Next + 1。spin_lock,无论如何,首先调用 LOCK 执行原子性操作、声明 ticket;而 trylock 则首先进行 slock.Next = slock.Owner 的判断,降低第二次比较调用 LOCK 的概率。08

27、0 static _always_inline int _ticket_spin_trylock(arch_spinlock_t *lock)081 082 int tmp, new;083 084 asm volatile(“movzwl %2, %0nt“085 “cmpb %h0,%b0nt“086 “leal 0x100(%“ REG_PTR_MODE “0), %1nt“087 “jne 1fnt“088 LOCK_PREFIX “cmpxchgw %w1,%2nt“089 “1:“090 “sete %b1nt“091 “movzbl %b1,%0nt“092 : “=095 09

28、6 return tmp;097 084 行 将 lock.slock 的值赋给 tmp。085 行 比较 tmp.next = tmp.owner,判断当前自旋锁是否空闲。086 行 leal 指令(Load effective address),实际上是 movl 的变形,“leal 0x10 (%eax, %eax, 3), %edx” “%edx = 0x10 + %eax + %eax * 3”,但 leal 却不像 movl 那样从内存取值、而直接读取寄存器。086 行 语句,根据 REG_PTR_MODE 不同配置,在 X86平台下为:“leal 0x100(%k0), %1”,

29、而在其它平台为:“leal 0x100(%q0), %1”,忽略占位符修饰“k”或“q” ,则该行语句等价于:“movl (%0 + 0x100),%1”,此时 new = tmp.Next + 1, tmp.Owner 。087 行 若 tmp.next != tmp.owner,即自旋锁不空闲,则跳到 089 行将 0 赋值给 tmp 并返回。088 行 原子性地执行操作 cmpxchgw,用以检测当前自旋锁是否已被其它 CPU 修改lock.slock 的 Next 域,若有竞争者则失败、否则获得该锁并将 Next + 1,这一系列操作是原子性的!cmpxchgw 操作解释如下:the

30、accumulator (8-32 bits) with “dest“. If equal the “dest“ is loaded with “src“, otherwise the accumulator is loaded with “dest“.(在 IA32 下,%EAX 即为累加器。)所以,“cmpxchgw %w1, %2”等效于:“tmp.Next = lock.slock.Next ? lock.slock = new : tmp = lock.slock”若 Next 未发生变化,则将 lock.slock 更新为 new, 实质上是将 slock 的 Next+1。090

31、 行 执行 sete 指令,若 cmpxchgw 或 cmpb 成功则将 new 的最低字节%b1 赋值为1,否则赋值为 0. sete 的解释为:Sets the byte in the operand to 1 if the Zero Flag is set, otherwise sets the operand to 0.091 行 movzbl(movz from byte to long)指令将%b1 赋值给 tmp 最低字节,且其它位补0.即将 tmp 置为 0 或 1.SMP 体系架构 -SPIN UNLOCK (ticket_shif 8)/include/linux/spin

32、lock_api_smp.h046 #ifdef CONFIG_INLINE_SPIN_LOCK047 #define _raw_spin_lock(lock) _raw_spin_lock(lock)048 #endif149 static inline void _raw_spin_unlock(raw_spinlock_t *lock)150 151 spin_release(152 do_raw_spin_unlock(lock);153 preempt_enable();154 spin_unlock 即最终调用 do_raw_spin_unlock 对自旋锁进行释放操作。/incl

33、ude/linux/spinlock.h136 static inline void do_raw_spin_lock(raw_spinlock_t *lock) _acquires(lock)137 138 _acquire(lock);139 arch_spin_lock(140 对于 x86 的 IA32 平台,arch_spin_lock 实现如下:/arch/x86/include/asm/spinlock.h198 static _always_inline void arch_spin_unlock(arch_spinlock_t *lock)199 200 _ticket_sp

34、in_unlock(lock);201 058 #if (NR_CPUS slock)103 :104 : “memory“, “cc“);105 101 行 将 lock-slock 的 Owner + 1,表示可以让下一个拥有牌号的 CPU 加锁。030 #if defined(CONFIG_X86_32) & 031 (defined(CONFIG_X86_OOSTORE) | defined(CONFIG_X86_PPRO_FENCE)032 /*033 * On PPro SMP or if we are using OOSTORE, we use a locked operation to unlock034 * (PPro errata 66, 92)035 */036 # define UNLOCK_LOCK_PREFIX LOCK_PREFIX037 #else038 # define UNLOCK_LOCK_PREFIX039 #endif

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 高等教育 > 大学课件

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报