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:
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(对称多处理)系统的所有处理器上都是原子执行的。这些操作执行速度极快 —— 只要有可能,它们都会被编译为单条机器指令。
/*
* 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 变量的操作,仍需借助其他锁机制。例如以下代码:
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 已从第一个原子变量中减去,但尚未加到第二个原子变量中。如果这段时间内并发执行的代码可能因这种状态出现异常,就必须引入某种锁机制来保护。
补充说明:
-
atomic_t 的核心特性
-
原子性:所有操作均通过单条原子指令实现(如 x86 的
lock前缀指令),避免多 CPU 并发修改导致的竞争; -
架构兼容性:
<asm/atomic.h>会根据不同 CPU 架构(x86/ARM/RISC-V)提供适配实现,上层代码无需关注硬件差异; -
24 位限制:早期内核为适配部分嵌入式架构(如 ARMv5),将 atomic_t 的有效位限制为 24 位,现代内核虽放宽限制,但仍建议避免存储超过 24 位的数值(保证跨架构兼容性)。
-
-
常见错误规避
-
禁止直接操作:不能通过
v->counter(内部成员)直接修改 atomic_t,必须使用内核提供的接口,否则会破坏原子性; -
多变量原子操作:多个 atomic_t 的组合操作(如转账场景:A 减金额、B 加金额)不具备原子性,需额外加锁(自旋锁 / 信号量);
-
返回值场景:若需获取操作后的新值,使用
atomic_add_return等带 return 的接口,避免 “先 read 再操作” 的非原子逻辑。
-
-
适用场景
-
计数器:如设备操作计数、中断次数统计、引用计数(替代 refcount_t,后者更安全);
-
状态标记:如用 0/1 表示 “忙 / 闲” 状态,通过
atomic_inc_and_test判断是否变为 0; -
轻量级同步:单变量的原子操作,替代锁以降低开销(如简单的标志位修改)。
-
-
现代内核扩展
-
64 位原子变量:内核提供
atomic64_t(定义在<linux/types.h>),支持 64 位整数的原子操作,接口与 atomic_t 一致(如atomic64_inc); -
refcount_t:专门用于引用计数的原子类型,提供更安全的检查(如防止减到负数),替代 atomic_t 做引用计数管理。
-
-
性能对比
-
atomic_t 操作:纳秒级开销,接近普通整型运算;
-
自旋锁:需禁用抢占 / 中断,开销约为 atomic_t 的 10~20 倍;
-
信号量:涉及睡眠 / 唤醒,开销为 atomic_t 的百倍以上。因此,单变量的并发修改优先使用 atomic_t,而非锁机制。
-
112

被折叠的 条评论
为什么被折叠?



