[源码分析][Linux]READ_ONCE与WRITE_ONCE

本文详细解释了原子操作宏READ_ONCE和WRITE_ONCE的功能与实现原理,这两种宏确保了对内存中变量的一次性读写,避免了由于CPU缓存机制导致的竞争条件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

READ_ONCE


#define READ_ONCE(x) __READ_ONCE(x, 1)

#define __READ_ONCE(x, check)                       \
({                                  \
    union { typeof(x) __val; char __c[1]; } __u;            \
    if (check)                          \
        __read_once_size(&(x), __u.__c, sizeof(x));     \
    else                                \
        __read_once_size_nocheck(&(x), __u.__c, sizeof(x)); \
    __u.__val;                          \
})

static __always_inline
void __read_once_size(const volatile void *p, void *res, int size)
{
    __READ_ONCE_SIZE;
}

static __always_inline
void __read_once_size_nocheck(const volatile void *p, void *res, int size)
{
    __READ_ONCE_SIZE;
}

#define __READ_ONCE_SIZE                        \
({                                  \
    switch (size) {                         \
    case 1: *(__u8 *)res = *(volatile __u8 *)p; break;      \
    case 2: *(__u16 *)res = *(volatile __u16 *)p; break;        \
    case 4: *(__u32 *)res = *(volatile __u32 *)p; break;        \
    case 8: *(__u64 *)res = *(volatile __u64 *)p; break;        \
    default:                            \
        barrier();                      \
        __builtin_memcpy((void *)res, (const void *)p, size);   \
        barrier();                      \
    }                               \
})

barrier() __asm__ __volatile__("": : :"memory")

WRITE_ONCE


#define WRITE_ONCE(x, val) \
({                          \
    union { typeof(x) __val; char __c[1]; } __u =   \
        { .__val = (__force typeof(x)) (val) }; \
    __write_once_size(&(x), __u.__c, sizeof(x));    \
    __u.__val;                  \
})

static __always_inline void __write_once_size(volatile void *p, void *res, int size)
{
    switch (size) {
    case 1: *(volatile __u8 *)p = *(__u8 *)res; break;
    case 2: *(volatile __u16 *)p = *(__u16 *)res; break;
    case 4: *(volatile __u32 *)p = *(__u32 *)res; break;
    case 8: *(volatile __u64 *)p = *(__u64 *)res; break;
    default:
        barrier();
        __builtin_memcpy((void *)p, (const void *)res, size);
        barrier();
    }
}

说明


在某些情况下CPU对内存中变量读写并不是一次完成的,这可能会出现竞争。而READ_ONCE和WRITE_ONCE实现对变量一次性读取和一次性写入。

<think>首先,用户的问题是基于之前的引用内容,关于Linux内核中的READ_ONCE宏。用户问:"我想理解READ_ONCELinux内核中的含义和用途 请问Linux内核中READ_ONCE的作用是什么"。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式。但在这个问题中,没有数学表达式,所以不需要。 - 独立公式必须使用$$...$$格式并单独成段。同样,这里没有数学公式。 - LaTeX语法正确:确保任何数学表达都正确。 - 使用中文回答:整个回答必须用中文。 - 生成相关问题:在回答后,需要生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识:例如,[^1]。引用来自提供的引用内容。 引用内容: - [^1]: 在Linux内核代码中,使用READ_ONCE宏读取变量,而不是直接读取。 - [^2]: __READ_ONCE宏返回局部联合体变量的值,确保读取正确的值。 - [^3]: READ_ONCEWRITE_ONCE更精确地让编译器忘记指定内存位置的内容,而barrier()丢弃所有缓存值。 - [^4]: READ_ONCE取代了ACCESS_ONCE,以避免编译器bug,防止编译器优化。 - [^5]: list_for_each_entry_rcu使用READ_ONCE实现高效无锁遍历。 用户的需求是理解READ_ONCE的含义和用途。所以,我需要基于这些引用,构建一个清晰、结构化的回答。 回答结构: 1. **介绍READ_ONCE**:简要解释什么是READ_ONCE及其基本作用。 2. **详细作用**:解释为什么需要它,基于引用内容。 3. **如何工作**:描述其机制。 4. **引用标识**:在相关部分添加引用标识。 最后,生成相关问题。 关键点来自引用: - 避免编译器优化:READ_ONCE确保在读取变量时,编译器不会缓存值或重新排序指令,从而在多线程环境中保证数据一致性。[^4] - 更精确:barrier()相比,READ_ONCE只针对特定内存位置,而不是所有缓存值。[^3] - 防止bug:取代ACCESS_ONCE,解决编译器bug。[^4] - 在RCU中使用:如list_for_each_entry_rcu,确保安全遍历。[^5] - 机制:通过定义一个局部联合体变量,返回其值,确保读取正确。[^2] 回答结构: - 开头:直接回答用户问题。 - 主体:逐步解释。 - 结尾:总结。 然后,生成相关问题。 用中文回答。</think>### Linux内核中READ_ONCE宏的作用 在Linux内核开发中,`READ_ONCE`宏是一个关键工具,用于在多线程和并发环境中安全地读取共享变量。它的核心作用是**防止编译器优化导致的读取不一致问题**,确保在并发访问时,读取操作是原子的和可预测的。下面我将逐步解释其含义、用途和工作原理,帮助您全面理解。 #### 1. **为什么需要READ_ONCE?** 在并发编程中,多个CPU核心或线程可能同时访问同一个共享变量。如果直接使用普通读取(如`int value = shared_var;`),编译器可能会进行优化,例如: - **缓存值**:编译器可能将变量值缓存在寄存器中,导致后续读取不是从内存中获取最新值。 - **指令重排**:编译器可能重新排序指令,破坏读取操作的顺序性。 这会导致数据不一致,例如在读取共享计数器或链表指针时,出现脏读或错误值。`READ_ONCE`宏通过强制编译器每次从内存中读取最新值来解决这个问题[^1][^3]。 #### 2. **READ_ONCE的主要作用** - **确保读取的原子性和一致性**:`READ_ONCE`宏确保每次读取都直接从内存位置获取值,避免编译器缓存或优化。这在无锁数据结构和RCU(Read-Copy-Update)机制中尤为重要,例如在`list_for_each_entry_rcu`中遍历链表时,它保证读取的指针是有效的[^5]。 - **防止编译器bug**:早期Linux内核使用`ACCESS_ONCE`宏,但后来发现编译器bug会导致优化失效。`READ_ONCE`作为替代方案,更可靠地处理这些情况,通过临时添加`volatile`语义来防止编译器优化[^4]。 - **精确控制内存访问**:通用屏障(如`barrier()`)相比,`READ_ONCE`更精确。`barrier()`会让编译器丢弃所有缓存值,而`READ_ONCE`只针对指定内存位置,减少性能开销[^3]。例如: - 直接读取:可能被优化为缓存值。 - 使用`READ_ONCE`:强制从内存读取,确保数据是最新的。 #### 3. **READ_ONCE的工作原理** `READ_ONCE`宏通过定义一个局部联合体变量来实现安全读取。其内部机制大致如下(基于Linux内核源码简化): - **步骤**:当读取一个变量时(如`int *ptr`),`READ_ONCE`会创建一个临时联合体变量,将目标变量复制到这个联合体中,然后返回其值。这确保了读取操作不被编译器优化打断。 - **代码示例**(简化说明): ```c #define READ_ONCE(x) ({ \ union { typeof(x) __val; } __u; \ // 定义一个联合体来存储值 __u.__val = (x); \ // 从内存复制值 __u.__val; \ // 返回复制的值 }) ``` 这样,编译器必须生成代码来实际读取内存位置,而不是依赖缓存[^2][^4]。 - **效果**:在多核系统中,如果变量被另一个线程修改,`READ_ONCE`保证读取的是最新值,避免数据竞争。 #### 4. **典型应用场景** - **RCU机制**:在读取共享数据时(如链表遍历),`READ_ONCE``rcu_read_lock()`配合,实现“读不加锁”的高效并发[^5]。 - **内核同步原语**:在自旋锁或信号量保护的区域外,读取简单变量时使用,减少锁开销。 - **设备驱动**:读取硬件寄存器状态,确保值不被优化。 总之,`READ_ONCE`在Linux内核中的作用是**提供一种轻量级、可靠的方式来读取共享变量,防止编译器优化带来的并发问题**。它通过精确的内存访问控制,提升了内核的并发性能和稳定性[^1][^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值