ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

#125_Liunx的原子锁

#125_Liunx的原子锁 Linux 原子操作详解从概念到内核实现一、什么是原子操作为什么需要它二、原子操作的实现原理与硬件支持1. 硬件原子指令(1) x86 架构(2) ARM 架构(3) RISC-V 架构2. 内存屏障三、Linux 内核中的原子操作 API1. 原子整型变量操作2. 原子位操作3. 比较并交换CAS四、原子锁利用原子操作实现锁五、原子操作与自旋锁、互斥锁的关系1. 层次关系2. 区别对比六、原子操作的典型应用场景七、面试常见问题与回答要点1. 什么是原子操作它解决了什么问题2. 原子操作是如何实现的3. 原子操作和锁有什么区别什么时候用哪个4. 请解释 CASCompare-And-Swap的原理和用途。5. Linux 内核中 atomic_t 和普通 int 有什么区别6. 什么是内存屏障原子操作需要它吗八、总结一、什么是原子操作为什么需要它1、原子操作Atomic Operation是指不可被中断的一个或一系列操作即该操作在执行过程中不会被其他线程或 CPU 打断要么全部执行完成要么完全不执行不会出现中间状态。在计算机科学中“原子”一词借自物理学表示不可再分割的最小单位。2、关注原子操作Atomic Operation的意义如果确定某个操作是原子的, 就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁. - (巧妙的利用原子操作和实现无锁编程)借助原子操作可以实现互斥锁(mutex). (linux中的mutex_lock_t)借助互斥锁, 可以实现让更多的操作变成原子操作.以简单的自增操作为例在 C 语言中count看似是一条语句但在汇编级别可能被拆分为多条指令// CN: count 在汇编级别可能被拆分为三条指令 -- EN: count may be split into three instructions at assembly levelldr r0,[r1]/* CN: 从内存加载 count 到寄存器 -- EN: Load count from memory to register */add r0,r0,#1/* CN: 寄存器加 1 -- EN: Add 1 to register */str r0,[r1]/* CN: 将结果写回内存 -- EN: Store result back to memory */如果两个线程同时执行count可能出现以下竞态线程 A 和线程 B 都读到了旧值各自加 1 后写回最终count只增加了 1而不是 2。这种问题就是竞态条件Race Condition它会导致数据不一致、程序行为不可预测。原子操作通过CPU 硬件提供的特殊指令保证上述“读-修改-写”序列要么整体完成要么不执行从而从根本上消除竞态。它不需要使用锁避免了锁带来的死锁风险和上下文切换开销因此是内核中最基础的同步原语。二、原子操作的实现原理与硬件支持1. 硬件原子指令不同 CPU 架构提供了不同的原子指令来支持原子操作。这些指令能够在一个总线周期内完成对内存的读-修改-写并保证在多处理器系统中内存的一致性。(1) x86 架构x86 使用LOCK前缀配合某些指令如inc、xchg、cmpxchg来实现原子操作。LOCK前缀会锁定总线或缓存行确保指令执行期间其他处理器无法访问该内存位置。(2) ARM 架构ARM 早期使用swp交换指令后来引入ldrex/strex独占加载/独占存储对。ldrex从内存加载值并标记独占访问strex尝试写回如果标记失效说明有其他核修改过则写回失败需要重试。(3) RISC-V 架构RISC-V 使用lr.w加载保留和sc.w条件存储实现类似 ARM 的独占访问机制。2. 内存屏障原子操作通常需要配合**内存屏障Memory Barrier**来保证操作的顺序性和可见性。内存屏障可以防止编译器和 CPU 对指令进行重排确保原子操作前后的内存访问按照预期顺序执行。在 x86 上LOCK指令本身就隐含了完全屏障而在 ARM 等弱内存序架构上可能需要显式使用dmb、dsb等指令。三、Linux 内核中的原子操作 APILinux 内核通过linux/atomic.h提供了一系列原子操作接口其中最常用的是atomic_t类型32 位原子变量及其操作函数。1. 原子整型变量操作atomic_tcount;/*CN: 定义原子变量 --EN: Define atomic variable*/atomic_set(count,0);/*CN: 设置值为 0 --EN: Set to 0*/atomic_inc(count);/*CN: 原子递增 --EN: Atomic increment*/atomic_dec(count);/*CN: 原子递减 --EN: Atomic decrement*/intvalatomic_read(count);/*CN: 读取当前值原子读取 --EN: Read current value (atomic read)*/这些函数保证在多核环境下对count的修改是原子的开发者无需额外加锁即可安全使用。2. 原子位操作针对内存中的某一位进行原子设置、清除和测试set_bit(nr,addr);/*CN: 原子设置第 nr 位 --EN: Atomically set bit nr*/clear_bit(nr,addr);/*CN: 原子清除第 nr 位 --EN: Atomically clear bit nr*/test_and_set_bit(nr,addr);/*CN: 原子测试并设置返回旧值 --EN: Atomically test and set, return old value*/常用于管理标志位、状态位避免使用锁。3. 比较并交换CAScmpxchg是实现无锁数据结构和复杂原子操作的基石intcmpxchg(ptr,old,new);/*CN: 如果 *ptr old则 *ptr new返回旧值 --EN: If *ptr equals old, set *ptr to new, return old value*/通过 CAS 可以构建自旋锁、无锁队列等高级并发结构。四、原子锁利用原子操作实现锁原子锁并不是 Linux 内核中一个独立的锁类型而是指基于原子操作实现的锁机制。最典型的例子就是自旋锁Spinlock它用原子操作来实现加锁和解锁的互斥。下面是一个自旋锁的简化实现展示原子操作如何用于构建锁typedefstruct{atomic_tlock;/*CN: 锁变量0 表示未锁1 表示已锁 --EN: Lock variable, 0 unlocked, 1 locked*/}spinlock_t;voidspin_lock(spinlock_t*lock){while(atomic_cmpxchg(lock-lock,0,1)!0){/* CN: 锁被占用自旋等待 -- EN: Lock held, spin wait */}}voidspin_unlock(spinlock_t*lock){atomic_set(lock-lock,0);}这里atomic_cmpxchg尝试将锁变量从 0 改为 1如果返回旧值 0 表示加锁成功否则表示锁已被他人持有继续循环。这个过程中没有睡眠也没有复杂的内核调度完全依赖原子操作保证了加锁的互斥性。因此原子操作是构建锁的基石而“原子锁”可以理解为用原子操作实现的轻量级锁或者泛指利用原子操作提供的无锁同步能力。五、原子操作与自旋锁、互斥锁的关系1. 层次关系原子操作是最底层的同步原语自旋锁和互斥锁都建立在原子操作之上自旋锁通过原子操作实现忙等待。互斥锁结合原子操作和睡眠/唤醒机制当获取锁失败时让线程睡眠。2. 区别对比特性原子操作自旋锁互斥锁阻塞行为不阻塞单条指令完成忙等待睡眠等待保护范围单个变量或位短临界区较长临界区使用复杂度简单中等复杂是否可睡眠不可不可可以典型场景引用计数、状态标志保护结构体、链表保护文件、设备选择原则对于单一数据项如计数、标志优先使用原子操作对于由多条指令组成的短临界区使用自旋锁对于可能睡眠的长临界区使用互斥锁。六、原子操作的典型应用场景1. 引用计数Linux 内核大量使用atomic_t管理对象引用计数如kref、文件描述符。当计数归零时释放对象原子操作确保计数增减不会因竞争而出错。2. 状态标志使用原子位操作管理设备状态、中断标志等无需加锁即可安全地查询和修改。3. 无锁数据结构利用 CAS 操作实现无锁栈、无锁队列避免锁的开销和死锁。4. 统计信息内核中网络包计数、性能计数器等常使用原子操作保证统计准确且开销低。七、面试常见问题与回答要点1. 什么是原子操作它解决了什么问题回答要点原子操作是不可分割的读-修改-写操作执行过程中不会被其他 CPU 或线程打断。解决多核环境下的竞态条件避免使用重量级锁。例子atomic_inc保证自增不丢失。2. 原子操作是如何实现的回答要点依赖 CPU 硬件指令如 x86 的LOCK前缀、ARM 的ldrex/strex。内核封装成 API如atomic_t和cmpxchg。可能包含内存屏障保证顺序。3. 原子操作和锁有什么区别什么时候用哪个回答要点原子操作无锁、无阻塞但只能保护单一变量或简单操作锁能保护复杂临界区。对于简单计数、标志位优先用原子操作对于多步操作用自旋锁或互斥锁。4. 请解释 CASCompare-And-Swap的原理和用途。回答要点比较内存值与旧值相等则更新为新值返回旧值。用于实现无锁数据结构、自旋锁。存在 ABA 问题通常用版本号解决。5. Linux 内核中atomic_t和普通int有什么区别回答要点atomic_t的操作是原子的普通int自增在多核下可能丢失更新。atomic_t通常不能直接用赋值需用atomic_set。6. 什么是内存屏障原子操作需要它吗回答要点内存屏障防止编译器或 CPU 对指令重排保证操作的顺序和可见性。许多原子操作隐含内存屏障如 x86 的lock指令但有些弱内存序架构需要显式屏障。八、总结原子操作是 Linux 内核并发控制的基石它利用硬件原子指令以极低的开销保证单一数据操作的原子性。理解“什么是原子操作”是理解一切锁机制和无锁编程的前提。通过原子操作我们可以构建自旋锁、互斥锁甚至实现复杂的无锁数据结构。在嵌入式 Linux 开发中掌握原子操作能够帮助开发者写出高效、安全的多线程代码也是面试中展现底层功底的必备知识点。
返回列表