READ_ONCE 是 Linux 内核中的一个重要宏定义,用于确保从内存中读取变量值时编译器不会对读取操作进行优化,从而保证读取操作的原子性[1][2]。这个宏在多线程、中断上下文或需要防止编译器优化的场景中尤为重要,可以确保数据的一致性和正确性。
READ_ONCE 的定义和工作原理
READ_ONCE 宏的定义如下:
#define READ_ONCE(x) (*(volatile typeof(x) *)&(x))
这个定义包含几个关键部分:
typeof(x)
:GNU 扩展,用于获取变量 x 的类型[1][2]volatile
:关键字告诉编译器不要对变量的读取进行优化,确保每次访问都直接从内存中读取[1][2]&(x)
:获取变量 x 的地址[1][2]*(...)
:对上述操作结果进行解引用,从而获取变量的值[1][2]
使用场景和示例
在多线程或中断上下文中,当需要读取共享变量并确保读取的是最新值时,可以使用 READ_ONCE:
int data;
// 在线程或中断上下文中读取 data 的值
int value = READ_ONCE(data);
这样可以保证每次读取 data
时都能获取到最新的值,避免编译器优化导致的问题[1][2]。
READ_ONCE 的保证与限制
READ_ONCE 提供以下保证:
- 强制编译器生成读指令,防止编译器优化掉读取操作[3][4]
- 确保从内存中直接读取变量的值,而不是使用寄存器中的缓存值[1][2]
然而,READ_ONCE 也有一些限制:
- 它仅保证读取操作的原子性和最新性,对于更复杂的并发控制,仍需要使用锁或其他同步机制[1][2]
- 尽管 READ_ONCE 强制编译器生成读指令,但它不强制编译器使用结果[3][4]
- 它不能保证 CPU 执行顺序,只能防止编译器优化[8]
与 WRITE_ONCE 的关系
对于写操作,Linux 内核中有对应的 WRITE_ONCE 宏,其定义方式和用途类似[1][2]。这两个宏共同工作,可以在内核编程中更安全地访问共享变量,避免数据竞争和内存一致性问题。
历史演变
从 Linux 内核 v4.15 开始,对 DEC Alpha 架构的 READ_ONCE() 添加了一个 smp_mb() 操作[4],这意味着在 DEC Alpha 上,READ_ONCE() 还会发出内存屏障指令,进一步保证内存访问的顺序性。
在早期,Linux 内核使用 ACCESS_ONCE() 宏,但后来被 READ_ONCE() 替代,主要是为了避免某些编译器的 bug[6]。
Citations:
[1] https://www.cnblogs.com/linhaostudy/p/18220391
[2] https://cloud.tencent.com/developer/article/2423158
[3] http://wangzhou.github.io/Linux%E5%86%85%E6%A0%B8memory-barrier%E6%96%87%E6%A1%A3%E5%AD%A6%E4%B9%A0/
[4] https://github.com/cncounter/translation/blob/master/tiemao_2020/46_Linux_Kernel_Memory_Barriers/README.md
[5] https://blog.csdn.net/shift_wwx/article/details/130082519
[6] https://blog.csdn.net/denglin12315/article/details/124584159
[7] https://cloud.tencent.com/developer/article/2146050
[8] https://juejin.cn/post/6844903479366909960