18 同步与异常(四) 原子操作
1 简介
原子操作此操作是不会被打断的
2 ldrex、strex、teq
-
ldrex
相对ldr而言此命令多了ex(exclude排除),意为独占
eg: 将会对ldr r0, r1命令中的r1标记为独占
-
strex
相较于str基本功能而言多出清除独占标记
eg: strex r2, r0, r1
将r0写入r1,并清除r1的独占标记。成功将r2设为0,如果r1已经被清掉了独占择将r2设为1
-
teq
测试两个操作数是否相等
3 常用API和数据结构
3.1 原子变量数据结构
本质上原子变量是一个结构体
typedef struct {
int counter;
} atomic_t;
3.2 原子操作
原子的操作变量v需要为原子变量atomic_t
-
atomic_read(v)
读出原子操作
-
atomic_set(v)
设置原子值
-
atomic_inc(v)
自加
-
atomic_dec(v)
自减
-
atomic_add(i, v)
加i
-
atomic_sub(i, v)
减i
-
atomic_inc_and_test(v)
先加1,判断是否等于0。如果等于0就返回1
-
atomic_dec_and_test(v)
先减1,判断是否等于0。如果等于0就返回1
-
set_bit(nr, p)
设置p的nr为1
-
clear_bit(nr, p)
设置p的nr为0
-
change_bit(nr, p)
改变p的nr反转
-
test_and_set_bit(nr, p)
设置p的nr为1,但是返回改位老值
-
test_and_clear_bit(nr, p)
设置p的nr为0,但是返回该位老值
-
test_and_change_bit(nr, p)
设置p的nr反转,返回该位的老值
4 内核原子操作的实现
原子操作分为单核和SMP两种类型,即ARMv6以上和以下
原子加减的实现是通过宏进行展开实现相关函数的定义的
#define ATOMIC_OPS(op, c_op, asm_op) \
ATOMIC_OP(op, c_op, asm_op) \
ATOMIC_OP_RETURN(op, c_op, asm_op) \
ATOMIC_FETCH_OP(op, c_op, asm_op)
ATOMIC_OPS(add, +=, add)
ATOMIC_OPS(sub, -=, sub)
4.1 单核实现逻辑
直接关中断
#define ATOMIC_OP(op, c_op, asm_op) \
static inline void atomic_##op(int i, atomic_t *v) \
{ \
unsigned long flags; \
\
raw_local_irq_save(flags); /* 关中断,并保存当前状态 */ \
v->counter c_op i; \
raw_local_irq_restore(flags); /* 恢复之前终端状态 */ \
}
4.2 SMP实现逻辑
#define ATOMIC_OP(op, c_op, asm_op) \
static inline void atomic_##op(int i, atomic_t *v) \
{ \
unsigned long tmp; \
int result; \
\
prefetchw(&v->counter); \
__asm__ __volatile__("@ atomic_" #op "\n" \
"1: ldrex %0, [%3]\n" @ 取出原子变量的值,并将原子变量的寄存器做好标记 \
" " #asm_op " %0, %0, %4\n" @ 将i的值与原子变量的值相加 \
" strex %1, %0, [%3]\n" @ 将计算结果放入原子变量,并尝试解锁之前的标记;结果存入%1寄存器 \
" teq %1, #0\n" @ %1寄存器如果等于0则向下指令,否则退出放回结果 \
" bne 1b" @ %1清标记失败,将跳转到1处继续执行 \
: "=&r" (result), "=&r" (tmp), "+Qo" (v->counter) \
: "r" (&v->counter), "Ir" (i) \
: "cc"); \
}
从汇编源码中可以看到如果原子操作的过程中存在并发操作,则会丢弃此次执行结果。
所以从宏观上看原子操作便是不可被中断的