原子服务

原子变量 是一个 32 比特的变量。线程或者 ISR 可以以不可打断的方式对其进行读取和修改。

概念

您可以定义任意数量的原子变量。

对原子变量的操作需要使用内核提供的原子相关的 API,这样才能保证所需要的操作能正确执行(即使高优先级的上下文也操作了同一变量)。

内核也支持在一个原子变量数组中对单一比特进行原子操作。

实现

定义原子变量

原子变量使用类型 atomic_t 进行定义。

默认情况下,原子变量被初始化为 0。不过,您也可以使用宏 ATOMIC_INIT 将其初始化为其它值:

atomic_t flags = ATOMIC_INIT(0xFF);

操作原子变量

您可以使用本节后面列举的 API 操作原子变量。

下面的代码向您展示了如何利用原子变量记录某个函数被调用的次数。由于 count 是原子递增的,因此可以避免这样的风险:当线程正在调用该函数时,对递增操作只进行了一半,此时有一个更高优先级的上下文抢占了该线程,且也调用了这个函数,从而导致递增值的错误。

atomic_t call_count;

int call_counting_routine(void)
{
    /* increment invocation counter */
    atomic_inc(&call_count);

    /* do rest of routine's processing */
    ...
}

操作原子变量数组

您可以以常规的方式定义一个原子变量数组(每个元素 32 比特)。不过,您也可以使用宏 ATOMIC_DEFINE 来定义一个 N 比特的原子数组。

本节最后面以 _bit() 结尾的 API 可以用来操作原子数组中的某一个比特。

下面的代码展示了如何通过原子数组的方式实现设置 200 个标志比特的方法。

#define NUM_FLAG_BITS 200

ATOMIC_DEFINE(flag_bits, NUM_FLAG_BITS);

/* set specified flag bit & return its previous value */
int set_flag_bit(int bit_position)
{
    return (int)atomic_set_bit(flag_bits, bit_position);
}

建议的用法

当只需要操作一个 32 比特的值时,使用一个原子变量即可。

当需要操作的标志比特位数超过 32 比特时,使用多个原子变量。

注解

相对于其它方法,例如互斥量或者锁中断,原子变量通常更高效。

配置选项

相关配置选项:

  • CONFIG_ATOMIC_OPERATIONS_BUILTIN
  • CONFIG_ATOMIC_OPERATIONS_CUSTOM
  • CONFIG_ATOMIC_OPERATIONS_C

APIs

文件 atomic.h 中提供了如下关于原子操作的 API:

  • ATOMIC_INIT
  • ATOMIC_DEFINE
  • atomic_get()
  • atomic_set()
  • atomic_clear()
  • atomic_add()
  • atomic_sub()
  • atomic_inc()
  • atomic_dec()
  • atomic_and()
  • atomic_or()
  • atomic_xor()
  • atomic_nand()
  • atomic_cas()
  • atomic_set_bit()
  • atomic_clear_bit()
  • atomic_test_bit()
  • atomic_test_and_set_bit()
  • atomic_test_and_clear_bit()