第5章:并发与竞态条件-15:Atomic Variables

In continuation of the previous text 第5章:并发与竞态条件-14:Alternatives to Locking, let's GO ahead.

Atomic Variables

Sometimes, a shared resource is a simple integer value. Suppose your driver main-

tains a shared variable n_op that tells how many device operations are currently out-

standing. Normally, even a simple operation such as:

cpp 复制代码
n_op++;

would require locking. Some processors might perform that sort of increment in an

atomic manner, but you can't count on it. But a full locking regime seems like over-

head for a simple integer value. For cases like this, the kernel provides an atomic

integer type called atomic_t, defined in <asm/atomic.h>.

有时,共享资源只是一个简单的整数值。假设你的驱动维护着一个共享变量 n_op,用于记录当前未完成的设备操作数量。正常情况下,即便是 n_op++ 这样简单的操作,也需要加锁保护 ------ 有些处理器可能会以原子方式执行这类自增操作,但你不能对此抱有依赖。不过,为一个简单整数值引入完整的锁机制显得开销过大。针对这类场景,内核提供了一种名为 atomic_t 的原子整数类型,定义在 <asm/atomic.h> 头文件中。

An atomic_t holds an int value on all supported architectures. Because of the way

this type works on some processors, however, the full integer range may not be avail-

able; thus, you should not count on an atomic_t holding more than 24 bits. The fol-

lowing operations are defined for the type and are guaranteed to be atomic with

respect to all processors of an SMP computer. The operations are very fast, because

they compile to a single machine instruction whenever possible.

atomic_t 在所有受支持的架构上均存储一个整型值,但由于部分处理器的实现机制限制,其无法使用整数的完整取值范围;因此,你不应依赖 atomic_t 存储超过 24 位的数值。内核为该类型定义了一系列操作,这些操作能保证在 SMP(对称多处理)系统的所有处理器上都是原子执行的。这些操作执行速度极快 ------ 只要有可能,它们都会被编译为单条机器指令。

cpp 复制代码
/*
 * Set the atomic variable v to the integer value i. 
 * You can also initialize atomic values 
 * at compile time with the ATOMIC_INIT macro.
*/
void atomic_set(atomic_t *v, int i);
atomic_t v = ATOMIC_INIT(0);

// Return the current value of v.
int atomic_read(atomic_t *v);

/*
 * Add i to the atomic variable pointed to by v. 
 * The return value is void, because there is an
 * extra cost to returning the new value, 
 *and most of the time there's no need to know it.
*/
void atomic_add(int i, atomic_t *v);

// Subtract i from *v.
void atomic_sub(int i, atomic_t *v);

// Increment or decrement an atomic variable.
void atomic_inc(atomic_t *v);
void atomic_dec(atomic_t *v);

/* 
 * Perform the specified operation and test the result;
 * if, after the operation, the atomic value is 0, 
 * then the return value is true; otherwise, it is false. Note that
 * there is no atomic_add_and_test.
*/
int atomic_inc_and_test(atomic_t *v);
int atomic_dec_and_test(atomic_t *v);
int atomic_sub_and_test(int i, atomic_t *v);

/*
 * Add the integer variable i to v. 
 * The return value is true 
 * if the result is negative, false otherwise.
 */
int atomic_add_negative(int i, atomic_t *v);

/*Behave just like atomic_add and friends, 
 * with the exception that they return the
 * new value of the atomic variable to the caller.
 */
int atomic_add_return(int i, atomic_t *v);
int atomic_sub_return(int i, atomic_t *v);
int atomic_inc_return(atomic_t *v);
int atomic_dec_return(atomic_t *v);

As stated earlier, atomic_t data items must be accessed only through these functions.

If you pass an atomic item to a function that expects an integer argument, you'll get

a compiler error.

You should also bear in mind that atomic_t values work only when the quantity in

question is truly atomic. Operations requiring multiple atomic_t variables still

require some other sort of locking. Consider the following code:

如前所述,atomic_t 类型的数据必须仅通过上述函数访问。若你将原子变量传递给期望接收整型参数的函数,编译器会直接报错。

你还需注意:atomic_t 仅适用于 "操作本身可完全原子化" 的场景。涉及多个 atomic_t 变量的操作,仍需借助其他锁机制。例如以下代码:

cpp 复制代码
atomic_sub(amount, &first_atomic);
atomic_add(amount, &second_atomic);

There is a period of time where the amount has been subtracted from the first atomic

value but not yet added to the second. If that state of affairs could create trouble for

code that might run between the two operations, some form of locking must be

employed.

这段代码执行过程中,会存在一个时间窗口:amount 已从第一个原子变量中减去,但尚未加到第二个原子变量中。如果这段时间内并发执行的代码可能因这种状态出现异常,就必须引入某种锁机制来保护。

补充说明:

  1. atomic_t 的核心特性

    • 原子性:所有操作均通过单条原子指令实现(如 x86 的lock前缀指令),避免多 CPU 并发修改导致的竞争;

    • 架构兼容性:<asm/atomic.h> 会根据不同 CPU 架构(x86/ARM/RISC-V)提供适配实现,上层代码无需关注硬件差异;

    • 24 位限制:早期内核为适配部分嵌入式架构(如 ARMv5),将 atomic_t 的有效位限制为 24 位,现代内核虽放宽限制,但仍建议避免存储超过 24 位的数值(保证跨架构兼容性)。

  2. 常见错误规避

    • 禁止直接操作:不能通过v->counter(内部成员)直接修改 atomic_t,必须使用内核提供的接口,否则会破坏原子性;

    • 多变量原子操作:多个 atomic_t 的组合操作(如转账场景:A 减金额、B 加金额)不具备原子性,需额外加锁(自旋锁 / 信号量);

    • 返回值场景:若需获取操作后的新值,使用atomic_add_return等带 return 的接口,避免 "先 read 再操作" 的非原子逻辑。

  3. 适用场景

    • 计数器:如设备操作计数、中断次数统计、引用计数(替代 refcount_t,后者更安全);

    • 状态标记:如用 0/1 表示 "忙 / 闲" 状态,通过atomic_inc_and_test判断是否变为 0;

    • 轻量级同步:单变量的原子操作,替代锁以降低开销(如简单的标志位修改)。

  4. 现代内核扩展

    • 64 位原子变量:内核提供atomic64_t(定义在<linux/types.h>),支持 64 位整数的原子操作,接口与 atomic_t 一致(如atomic64_inc);

    • refcount_t:专门用于引用计数的原子类型,提供更安全的检查(如防止减到负数),替代 atomic_t 做引用计数管理。

  5. 性能对比

    • atomic_t 操作:纳秒级开销,接近普通整型运算;

    • 自旋锁:需禁用抢占 / 中断,开销约为 atomic_t 的 10~20 倍;

    • 信号量:涉及睡眠 / 唤醒,开销为 atomic_t 的百倍以上。因此,单变量的并发修改优先使用 atomic_t,而非锁机制。

相关推荐
Sean X14 小时前
Ubuntu24.04安装向日葵
linux·ubuntu
IT 乔峰16 小时前
脚本部署MHA集群
linux·shell
dz小伟16 小时前
execve() 系统调用深度解析:从用户空间到内核的完整加载过程
linux
Mr_Xuhhh16 小时前
博客标题:深入理解Shell:从进程控制到自主实现一个微型Shell
linux·运维·服务器
JoyCheung-16 小时前
Free底层是怎么释放内存的
linux·c语言
旖旎夜光17 小时前
Linux(9)
linux·学习
咕噜咕噜万18 小时前
ATDD实践:验收测试驱动开发的完整方法论与工具链
驱动开发
喵了meme18 小时前
Linux学习日记24:Linux网络编程基础
linux·网络·学习
whlqjn_121118 小时前
linux下使用SHC对Shell脚本进行封装和源码隐藏
linux·centos
weixin_4624462319 小时前
K8s 集群部署基础:Linux 三节点 SSH 互信(免密登录)配置指南
linux·kubernetes·ssh