news 2026/9/10 23:33:36

#125_Liunx的原子锁

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
#125_Liunx的原子锁

Linux 原子操作详解:从概念到内核实现

  • 一、什么是原子操作?为什么需要它?
  • 二、原子操作的实现原理与硬件支持
    • 1. 硬件原子指令
      • (1) x86 架构
      • (2) ARM 架构
      • (3) RISC-V 架构
    • 2. 内存屏障
  • 三、Linux 内核中的原子操作 API
    • 1. 原子整型变量操作
    • 2. 原子位操作
    • 3. 比较并交换(CAS)
  • 四、原子锁:利用原子操作实现锁
  • 五、原子操作与自旋锁、互斥锁的关系
    • 1. 层次关系
    • 2. 区别对比
  • 六、原子操作的典型应用场景
  • 七、面试常见问题与回答要点
    • 1. 什么是原子操作?它解决了什么问题?
    • 2. 原子操作是如何实现的?
    • 3. 原子操作和锁有什么区别?什么时候用哪个?
    • 4. 请解释 CAS(Compare-And-Swap)的原理和用途。
    • 5. Linux 内核中 `atomic_t` 和普通 `int` 有什么区别?
    • 6. 什么是内存屏障?原子操作需要它吗?
  • 八、总结

一、什么是原子操作?为什么需要它?

1、原子操作(Atomic Operation)是指不可被中断的一个或一系列操作,即该操作在执行过程中不会被其他线程或 CPU 打断,要么全部执行完成,要么完全不执行,不会出现中间状态。在计算机科学中,“原子”一词借自物理学,表示不可再分割的最小单位。
2、关注原子操作(Atomic Operation)的意义:

  1. 如果确定某个操作是原子的, 就不用为了去保护这个操作而加上会耗费昂贵性能开销的锁. - (巧妙的利用原子操作和实现无锁编程)
  2. 借助原子操作可以实现互斥锁(mutex). (linux中的mutex_lock_t)
  3. 借助互斥锁, 可以实现让更多的操作变成原子操作.

以简单的自增操作为例,在 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前缀配合某些指令(如incxchgcmpxchg)来实现原子操作。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 等弱内存序架构上,可能需要显式使用dmbdsb等指令。

三、Linux 内核中的原子操作 API

Linux 内核通过<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*/intval=atomic_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. 比较并交换(CAS)

cmpxchg是实现无锁数据结构和复杂原子操作的基石:

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_tcmpxchg
  • 可能包含内存屏障保证顺序。

3. 原子操作和锁有什么区别?什么时候用哪个?

回答要点

  • 原子操作无锁、无阻塞,但只能保护单一变量或简单操作;锁能保护复杂临界区。
  • 对于简单计数、标志位,优先用原子操作;对于多步操作,用自旋锁或互斥锁。

4. 请解释 CAS(Compare-And-Swap)的原理和用途。

回答要点

  • 比较内存值与旧值,相等则更新为新值,返回旧值。
  • 用于实现无锁数据结构、自旋锁。
  • 存在 ABA 问题,通常用版本号解决。

5. Linux 内核中atomic_t和普通int有什么区别?

回答要点

  • atomic_t的操作是原子的,普通int自增在多核下可能丢失更新。
  • atomic_t通常不能直接用=赋值,需用atomic_set

6. 什么是内存屏障?原子操作需要它吗?

回答要点

  • 内存屏障防止编译器或 CPU 对指令重排,保证操作的顺序和可见性。
  • 许多原子操作隐含内存屏障,如 x86 的lock指令;但有些弱内存序架构需要显式屏障。

八、总结

原子操作是 Linux 内核并发控制的基石,它利用硬件原子指令,以极低的开销保证单一数据操作的原子性。理解“什么是原子操作”是理解一切锁机制和无锁编程的前提。通过原子操作,我们可以构建自旋锁、互斥锁,甚至实现复杂的无锁数据结构。在嵌入式 Linux 开发中,掌握原子操作能够帮助开发者写出高效、安全的多线程代码,也是面试中展现底层功底的必备知识点。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 23:30:48

python笔记2

推导式:一句话构建数据结构 推导式是极具特色的“语法糖”用一行代码创建列表、字典、集合的简洁语法&#xff0c;‌它能替代多行循环&#xff0c;让代码更简洁高效 输入源&#xff1a;range list tuple set dict 输出源&#xff1a;list tuple set …

作者头像 李华
网站建设 2026/9/10 23:30:40

Simulink微电网经济调度建模与优化策略

1. 微电网经济调度优化策略概述微电网作为分布式能源系统的重要实现形式&#xff0c;其经济调度优化是确保系统可靠运行与经济效益最大化的关键技术。在Simulink环境下构建微电网经济调度模型&#xff0c;能够直观展现不同调度策略下的系统动态响应特性。微电网经济调度的核心目…

作者头像 李华
网站建设 2026/9/10 23:29:24

光伏系统MPPT控制与Simulink建模实践

1. 光伏系统最大功率跟踪的核心挑战 光伏电池的输出功率会随着光照强度、环境温度和负载条件的变化而波动。当光伏阵列直接连接负载时&#xff0c;工作点往往无法自动匹配最大功率点(MPP)&#xff0c;导致能量转换效率低下。这个问题在部分阴影条件下尤为明显&#xff0c;传统的…

作者头像 李华
网站建设 2026/9/10 23:27:08

【多智能体】基于 o3-mini 和 Gemini 的多模态 AI 编程智能体团队

目录 案例简介 案例目标 技术栈与核心依赖 编程语言与框架 核心依赖库 AI 模型 基础设施 项目结构 核心代码实现 1. 智能体架构设计 视觉智能体(Vision Agent) 编程智能体(Coding Agent) 执行智能体(Execution Agent) 2. 图像处理流程 3. 沙箱代码执行 4…

作者头像 李华