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");                                                        \
} 

从汇编源码中可以看到如果原子操作的过程中存在并发操作,则会丢弃此次执行结果。
所以从宏观上看原子操作便是不可被中断的

posted @ 2023-04-19 22:15  人民广场的二道贩子  阅读(26)  评论(0编辑  收藏  举报