In continuation of the previous text 第5章:并发与竞态条件-15:Atomic Variables, let's GO ahead.
Bit Operations
The atomic_t type is good for performing integer arithmetic. It doesn’t work as well,
however, when you need to manipulate individual bits in an atomic manner. For that
purpose, instead, the kernel offers a set of functions that modify or test single bits
atomically. Because the whole operation happens in a single step, no interrupt (or
other processor) can interfere.
atomic_t 类型适用于整数算术运算,但当你需要以原子方式操作单个比特位时,它就不再适用了。针对这类场景,内核提供了一组函数,可原子化地修改或测试单个比特位 —— 由于整个操作在单个步骤中完成,任何中断(或其他处理器)都无法干扰操作过程。
Atomic bit operations are very fast, since they perform the operation using a single
machine instruction without disabling interrupts whenever the underlying platform
can do that. The functions are architecture dependent and are declared in <asm/
bitops.h>. They are guaranteed to be atomic even on SMP computers and are useful
to keep coherence across processors.
原子位操作的执行速度极快:只要底层硬件平台支持,这些操作会通过单条机器指令完成,且无需禁用中断。这类函数与架构强相关,声明在 <asm/bitops.h> 头文件中;它们能保证在 SMP(对称多处理)系统中仍具备原子性,可有效维持多处理器间的数据一致性。
Unfortunately, data typing in these functions is architecture dependent as well. The
nr argument (describing which bit to manipulate) is usually defined as int but is
unsigned long for a few architectures. The address to be modified is usually a pointer
to unsigned long, but a few architectures use void * instead.
需要注意的是,这些函数的参数类型也与架构相关:
-
nr参数(指定要操作的比特位序号)通常定义为int类型,但部分架构下为unsigned long; -
待修改数据的地址参数,通常为指向
unsigned long的指针,但少数架构中使用void *类型。
The available bit operations are:
// 将addr指向的数据中第nr位设为1
void set_bit(nr, void *addr);
/*
* 将addr指向的unsigned long类型数据中第nr位清零。
* 其他语义与set_bit一致。
*/
void clear_bit(nr, void *addr);
// 翻转addr指向数据中第nr位的值(0变1,1变0)
void change_bit(nr, void *addr);
/*
* 该函数是唯一无需原子执行的位操作函数;
* 仅返回addr指向数据中第nr位的当前值。
*/
test_bit(nr, void *addr);
// 原子化设置第nr位,并返回该位操作前的原值
int test_and_set_bit(nr, void *addr);
// 原子化清零第nr位,并返回该位操作前的原值
int test_and_clear_bit(nr, void *addr);
/*
* 原子化翻转第nr位,语义与上述函数一致,
* 同时返回该位操作前的原值。
*/
int test_and_change_bit(nr, void *addr);
When these functions are used to access and modify a shared flag, you don’t have to
do anything except call them; they perform their operations in an atomic manner.
Using bit operations to manage a lock variable that controls access to a shared vari-
able, on the other hand, is a little more complicated and deserves an example. Most
modern code does not use bit operations in this way, but code like the following still
exists in the kernel.
当使用这些函数访问 / 修改共享标志位时,只需直接调用即可 —— 它们本身已保证操作的原子性。但如果想用位操作实现 “控制共享变量访问的锁”,逻辑会稍复杂,以下是一个示例说明(尽管现代内核代码已很少这样用,但内核中仍存在此类遗留代码)。
A code segment that needs to access a shared data item tries to atomically acquire a
lock using either test_and_set_bit or test_and_clear_bit. The usual implementation is
shown here; it assumes that the lock lives at bit nr of address addr. It also assumes
that the bit is 0 when the lock is free or nonzero when the lock is busy.
若代码段需要访问共享数据,可通过 test_and_set_bit 或 test_and_clear_bit 原子化获取锁。典型实现逻辑如下:
-
假设锁对应
addr地址的第nr位; -
约定:比特位为 0 表示锁空闲,非 0 表示锁被占用。
/* 尝试获取锁 */
while (test_and_set_bit(nr, addr) != 0)
wait_for_a_while(); // 锁被占用,等待
/* 执行临界区操作 */
/* 释放锁,并检查状态 */
if (test_and_clear_bit(nr, addr) == 0)
something_went_wrong(); // 锁已被释放:出现错误
If you read through the kernel source, you find code that works like this example. It
is, however, far better to use spinlocks in new code; spinlocks are well debugged,
- they handle issues like interrupts and kernel preemption, and others reading your
code do not have to work to understand what you are doing.
在内核源码中能找到类似上述逻辑的代码,但新代码应优先使用自旋锁而非位操作实现锁机制:自旋锁经过充分调试,能妥善处理中断、内核抢占等问题,且其他开发者阅读代码时,无需额外分析即可理解你的锁逻辑。
补充说明:
1. 原子位操作的核心特性
-
原子性:操作全程无中断 / 抢占干扰,多 CPU 并发修改同一位时不会出现竞争;
-
轻量级:无锁的上下文切换 / 自旋开销,仅单条硬件指令(如 x86 的
bts/btr指令); -
架构兼容性:
<asm/bitops.h>会根据 CPU 架构(x86/ARM/RISC-V)封装底层实现,上层代码无需适配。
2. test_bit 非原子性的原因
-
test_bit仅读取位值,不修改数据 —— 即使多 CPU 同时读取同一位,结果也不会出错,因此无需原子化;而set_bit/clear_bit等写操作必须原子化,否则会出现 “读 - 改 - 写” 竞争。
3. 位操作实现锁的缺陷
-
无抢占 / 中断保护:持有位锁时若发生中断,中断处理程序若也尝试获取该锁,会导致死锁;
-
无锁排序机制:多把位锁同时获取时,易出现死锁,且无统一的排序规则;
-
可读性差:需开发者自行约定位的含义,不如自旋锁语义清晰。
4. 原子位操作的正确适用场景
-
共享标志位:如设备 “忙 / 闲” 状态、中断 “使能 / 禁用” 标记;
-
位图管理:如内存页分配的位图、设备中断掩码的位操作;
-
轻量级状态切换:无需完整锁机制的单比特状态修改(如开关类操作)。
5. 现代内核的替代方案
-
锁机制:优先使用
spinlock_t/mutex_t替代位操作实现锁; -
标志位管理:仍可使用原子位操作,但仅用于纯状态标记,不用于临界区保护;
-
多比特操作:若需原子化修改多个比特位,需结合自旋锁 + 普通位操作。
2541

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



