- 论坛徽章:
- 0
|
本帖最后由 lli_njupt 于 2011-12-31 20:00 编辑
回复 7# onlyxuyang
仔细看了下Linux对原子位操作代码的实现,无论单CPU还是SMP系统,都是通过宏对ATOMIC_BITOP的扩展,
而对ATOMIC_BITOP的定义由是否配置CONFIG_SMP决定:
- 2.6.39.4版本为例
- arch/arm/include/asm/bitops.h
- #ifndef CONFIG_SMP
- /*
- * The __* form of bitops are non-atomic and may be reordered.
- */
- #define ATOMIC_BITOP(name,nr,p) \
- (__builtin_constant_p(nr) ? ____atomic_##name(nr, p) : _##name(nr,p))
- #else
- #define ATOMIC_BITOP(name,nr,p) _##name(nr,p)
- #endif
复制代码 以上的定义说明在单CPU系统上ATOMIC_BITOP的操作分两类:
1.在nr是常量时,调用____atomic_XXX系列函数,比如:- ____atomic_test_and_set_bit(unsigned int bit, volatile unsigned long *p)
- {
- unsigned long flags;
- unsigned int res;
- unsigned long mask = 1UL << (bit & 31);
- /* Point 1 */
- p += bit >> 5;
- /* Point 2 */
- raw_local_irq_save(flags);
- /* Point 3 */
- res = *p;
- *p = res | mask;
- /* Point 4 */
- raw_local_irq_restore(flags);
- return res & mask;
- }
复制代码 为何是在常量的时候才用这一系列函数呢?常量的bit可以使mask赋值在编译时完成,p += bit>>5 可以化简为一条add指令。
由于____atomic_test_and_set_bit整个函数都要保证原子操作,
那么其中不应该发生调度以及中断。显然只有在3和4之间的代码才能满足这个要求,从1到2之间的指令完全有可能被中断,
有意思的是这条用来实现把指针对齐到32的操作,会被编译器优化掉,不生成任何代码,这也要求bit是常量。
2.当bit不为常量时调用了与SMP相同的处理函数
显然此时调用_##name系列函数,比如_test_and_set_bit,
- arch/arm/lib/testsetbit.S
- ENTRY(_test_and_set_bit)
- testop orreq, streq
- ENDPROC(_test_and_set_bit)
复制代码 一些列的原子位操作都会调用通过汇编语言写成的testop来实现。它定义如下:
- arch/arm/lib/bitops.h
- #if __LINUX_ARM_ARCH__ >= 6
- .macro testop, instr, store
- ands ip, r1, #3
- strneb r1, [ip] @ assert word-aligned
- mov r2, #1
- and r3, r0, #31 @ Get bit offset
- mov r0, r0, lsr #5
- add r1, r1, r0, lsl #2 @ Get word offset
- mov r3, r2, lsl r3 @ create mask
- smp_dmb
- 1: ldrex r2, [r1]
- ands r0, r2, r3 @ save old value of bit
- \instr r2, r2, r3 @ toggle bit
- strex ip, r2, [r1]
- cmp ip, #0
- bne 1b
- smp_dmb
- cmp r0, #0
- movne r0, #1
- 2: bx lr
- .endm
复制代码 这里只摘了ARMv6和以上指令集的实现,现在很清楚的可以看到ldrex 和 strex,另外还有硬件内存屏障smp_dmb的使用了。
|
|