当前位置:首页>Linux>[程序员] Linux: READ_ONCE

[程序员] Linux: READ_ONCE

  • 2026-10-12 17:13:54
[程序员] Linux: READ_ONCE

接上一篇:[程序员] epoll与wait-queue的工作机制。除了回调的横跳,还看到以下的情况,所见非所得的问题。

比如READ_ONCE在Linux内核中是一个宏,它的主要作用是防止编译器对内存读取操作进行不当的优化,确保每次都从内存中实际读取值,而不是字面意义上的“只读一次”。这就是内核代码比较难读的另一个原因。

READ_ONCE是防止编译器优化,旨在告诉编译器,每次遇到这个宏时,都必须从内存中重新读取变量的值,而不是使用之前缓存的寄存器值或进行其他优化(例如,将多次读取合并为一次,或者重新排序读写操作)。

在多线程或中断上下文中,一个变量的值可能在任何时候被其他CPU或硬件修改。如果没有READ_ONCE,编译器可能会认为变量的值在两次读取之间不会改变,从而使用旧的缓存值,导致程序行为不正确。

它不是指变量在整个程序生命周期中只能被读取一次。它的意思是“每次调用READ_ONCE时,都执行一次新的、不受优化的内存读取”。你可以多次使用READ_ONCE来读取同一个变量,每次都会确保从内存中获取最新值。

与volatile的区别,C语言的volatile关键字也有防止编译器优化的作用,但它的语义更广,会影响所有对volatile变量的读写操作。

READ_ONCE在Linux内核中通常被认为是更精细和更安全的替代方案,因为它只影响读取操作,并且其行为在内核中经过了严格定义和测试,以确保跨不同架构的正确性。

从代码上看,READ_ONCE是创建了一个临时变量来存放变量的值,如果是简单类型变量,要加volatile,如果是复杂变量,要用memcpy来复制内存。

/* * Prevent the compiler from merging or refetching reads or writes. The * compiler is also forbidden from reordering successive instances of * READ_ONCE and WRITE_ONCE, but only when the compiler is aware of some * particular ordering. One way to make the compiler aware of ordering is to * put the two invocations of READ_ONCE or WRITE_ONCE in different C * statements. * * These two macros will also work on aggregate data types like structs or * unions. If the size of the accessed data type exceeds the word size of * the machine (e.g., 32 bits or 64 bits) READ_ONCE() and WRITE_ONCE() will * fall back to memcpy and print a compile-time warning. * * Their two major use cases are: (1) Mediating communication between * process-level code and irq/NMI handlers, all running on the same CPU, * and (2) Ensuring that the compiler does not fold, spindle, or otherwise * mutilate accesses that either do not require ordering or that interact * with an explicit memory barrier or atomic instruction that provides the * required ordering. */#define READ_ONCE(x)					\({							\	union { typeof(x) __val; char __c[1]; } __u =	\		{ .__c = { 0 } };			\	__read_once_size(&(x), __u.__c, sizeof(x));	\	__u.__val;					\})
static __always_inline void __read_once_size(const volatile void *p, void *res, int size){switch (size) {case 1: *(__u8_alias_t  *) res = *(volatile __u8_alias_t  *) p; break;case 2: *(__u16_alias_t *) res = *(volatile __u16_alias_t *) p; break;case 4: *(__u32_alias_t *) res = *(volatile __u32_alias_t *) p; break;case 8: *(__u64_alias_t *) res = *(volatile __u64_alias_t *) p; break;default:barrier();		__builtin_memcpy((void *)res, (const void *)p, size);barrier();	}}

最新文章

随机文章