63、内核同步:原子性、临界区与数据竞争

内核同步:原子性、临界区与数据竞争

1. 原子性与内核空间实现

在现代微处理器中,通常只有单条机器语言指令,或者对处理器总线宽度内对齐的基本数据类型的读写操作,才能保证原子性。在用户空间,想要让几行 C 代码实现真正的原子性是不可能的,不过可以通过构建一个采用 SCHED_FIFO 任务调度策略和实时(RT)优先级为 99 的用户线程,来接近原子性。这样,当该线程要运行时,除了硬件中断或异常外,几乎没有其他因素能抢占它。

而在内核空间,我们可以编写真正具有原子性的代码,常用的方法是使用自旋锁(spinlocks)。

2. 临界区要点总结

2.1 临界区定义

临界区是可能并发执行的代码路径,能并行运行,且会对共享可写数据(即共享状态)进行读写操作。

2.2 临界区特性

  • 需要保护 :由于操作共享可写数据,临界区必须免受并行和并发的影响,需以串行、互斥的方式运行。
  • 原子执行 :在原子非阻塞上下文(包括任何中断上下文)中运行时,必须保证其原子性,即不可分割地执行到完成,不被中断。保护后,在“解锁”前可安全访问共享状态。

2.3 识别与保护

  • 识别 :仔细审查代码,确保不遗漏临界区。任何全局或静态变量通常是明显的标志,但在可能并发的代码路径中,任何共享状态(如硬件寄存器、邮箱等)都可能是临界区。
  • 保护 :可通过多种技术实现,常见的是锁定,此外还有原子操作符和无锁编程技术。

2.4 常见错误

  • 仅保护写操作 :必须同时保护对共享可写数据的读操作,否则可能导致撕裂或脏读。例如,在 32 位系统上读写 64 位数据项时,操作不是原子的,若一个线程在读,另一个线程同时在写,可能会出现问题。
  • 使用错误的锁 :必须使用相同的(正确的)锁来保护给定的数据项。例如,若使用锁 A 保护全局数据结构 X,那么每次访问该结构时都必须使用锁 A。
  • 未保护临界区 :会导致数据竞争,其结果取决于运行时环境和时间,是一种难以发现、重现、确定根源和修复的缺陷。

2.5 例外情况

  • 处理局部变量 :局部变量分配在线程的私有栈(或中断上下文中的本地 IRQ 栈)上,本质上是安全的。
  • 处理序列化代码中的共享可写数据 :例如内核模块的 init 和 cleanup 方法,它们只会串行执行一次。
  • 处理真正的常量和只读共享数据 :但不要被 C 语言的 const 关键字误导。

2.6 锁定复杂性

锁定本质上很复杂,必须仔细设计和实现锁定方案,避免死锁。

3. 数据竞争的正式定义

3.1 内存一致性模型

内存(一致性)模型为多并发加载/存储操作时的内存一致性概念提供了更正式的方法,Linux 内核有自己的内存模型,即 Linux-Kernel 内存模型(LKMM)。

3.2 LKMM 中的内存访问类型

访问类型 描述 示例
普通访问 通过 C 语言语句对内存进行的典型访问 i ++ 或 y = 42 - x;
标记访问 旨在隐式保证原子性的“特殊”访问 - 加载: READ_ONCE(x);
- 存储: WRITE_ONCE(y, 42-x);

3.3 数据竞争的定义

当满足以下条件时,会发生数据竞争:
1. 两个内存访问访问同一位置。
2. 至少有一个是存储操作。
3. 至少有一个是普通访问。
4. 它们发生在不同的 CPU 上(或同一 CPU 上的不同线程中)。
5. 它们并发执行。

4. 数据竞争的检测与避免

4.1 标记访问的使用

标记访问虽能保证原子加载和存储,但不保证内存顺序,且会阻止像 KCSAN 这样的工具检测数据竞争,因此大多数情况下应继续使用普通 C 访问。

4.2 数据竞争检测工具

Kernel Concurrency Sanitizer(KCSAN)可用于检测数据竞争,建议学习如何使用它。

5. Linux 内核中的并发问题

5.1 并发问题来源

  • 对称多处理器(SMP)系统 :多个用户空间进程可同时执行驱动的代码,操作共享可写数据,可能导致数据竞争。即使是单核心系统,未来也可能向多核发展,且单核心系统也存在并发问题。
  • 可抢占内核 :在临界区内,内核可能抢占当前进程并切换到另一个进程,导致数据竞争。
  • 阻塞 I/O :在临界区内遇到阻塞调用时,当前进程会进入睡眠状态,若此时另一个进程执行相同代码路径,可能导致数据竞争。
  • 硬件中断 :硬件中断优先级高,会抢占当前代码执行。若中断处理代码和被中断的进程上下文操作相同的共享可写数据,会导致数据竞争。

5.2 并发问题示例

graph TD;
    A[进程 P1 进入临界区] --> B{是否发生硬件中断};
    B -- 是 --> C[中断处理代码尝试获取相同锁];
    C --> D[数据竞争];
    B -- 否 --> E[进程 P1 正常执行];

5.3 关键要点

识别和保护临界区是确保数据安全的关键,虽然并发问题看似复杂,但保护临界区的锁定 API 并不难学习使用。

6. 锁定准则与死锁问题

6.1 锁定准则

  • 锁定粒度 :临界区的长度应适中,既不能过粗(过长),也不能过细。在大型项目中,锁的数量过多或过少都有问题。锁太少会导致性能问题,因为相同的锁会被频繁使用,容易产生竞争;锁太多虽然有利于性能,但会增加复杂度。开发者需要清楚每个锁保护的共享数据对象,通常可以将保护数据结构的锁作为成员放在数据结构内部。此外,长时间的原子临界区可能会导致高延迟和性能瓶颈,可使用如 criticalstat[-bpfcc] 等工具来检测和报告。
  • 锁的释放规则 :只有持有锁的“所有者”才能释放(解锁)它,尝试释放未持有的锁或在持有锁时重新获取它被视为错误。虽然递归锁定可以解决部分问题,但通常不被推荐。
  • 锁的顺序 :锁的获取顺序至关重要,必须确保在整个代码中按照相同的顺序获取锁,否则可能导致死锁。而锁的释放顺序则无关紧要,但必须释放所有持有的锁,以防止饥饿。
  • 避免复杂性 :尽量避免复杂的锁定场景,保持简单性。

6.2 常见死锁场景

6.2.1 单锁,进程上下文
  • 自我死锁 :尝试两次获取同一把锁会导致自我死锁。例如,持有锁时再次尝试获取该锁,由于锁已被锁定,需要等待解锁,但自己又持有锁无法解锁,从而陷入死循环。
6.2.2 多锁,进程上下文
  • AB - BA 死锁 :线程 A 在 CPU 0 上获取锁 A 后想获取锁 B,同时线程 B 在 CPU 1 上获取锁 B 后想获取锁 A,双方相互等待,导致死锁。这种情况可以扩展为 AB - BC - CA 等更复杂的循环依赖。
6.2.3 单锁,进程和中断上下文
  • 自旋锁死锁 :进程 P1 在 CPU 核心 0 上获取锁 A 后,驱动的硬件中断发生,中断处理代码尝试获取同一把锁 A。由于锁 A 已被进程 P1 锁定,中断处理代码会等待解锁,但进程 P1 又因中断正在运行而无法解锁,最终导致死锁。解决方法是在获取锁时禁用本地核心的所有硬件中断,确保进程上下文在执行临界区时不被中断,执行完后再重新启用中断。
6.2.4 更复杂的情况

涉及多个锁以及进程和中断(硬中断和软中断)上下文的情况更为复杂,这里暂不详细展开。

6.2.5 死锁解决建议

在简单情况下,遵循锁的顺序准则通常足以避免死锁。例如,在代码中始终按照预先定义的顺序获取锁。但随着情况变得复杂,即使是经验丰富的开发者也可能会遇到问题。不过,Linux 内核的运行时锁依赖验证器 lockdep 可以捕获几乎所有的死锁情况。

7. 总结

7.1 关键知识点回顾

  • 原子性在用户空间难以保证,在内核空间可通过自旋锁实现。
  • 临界区是操作共享可写数据的并发代码路径,必须进行识别和保护,避免常见错误。
  • 数据竞争的发生条件由 LKMM 定义,可通过合适的访问方式和工具进行检测和避免。
  • Linux 内核中的并发问题源于 SMP 系统、可抢占内核、阻塞 I/O 和硬件中断,需要正确使用锁定技术来解决。
  • 锁定准则包括锁定粒度、锁的释放和顺序等,违反这些准则可能导致死锁。

7.2 重要性强调

理解和掌握内核同步的相关知识对于内核和驱动开发者至关重要。正确处理临界区和避免数据竞争、死锁等问题,能够确保系统的稳定性和性能。在实际开发中,要仔细设计和实现锁定方案,充分利用现有的工具和技术,不断提高代码的质量和可靠性。

7.3 未来展望

随着计算机系统的不断发展,内核同步技术也将不断演进。开发者需要持续关注新的技术和方法,以应对日益复杂的并发场景和性能需求。例如,未来可能会出现更高效的锁定机制和更强大的检测工具,帮助开发者更好地解决内核同步问题。

以下是一个总结内核同步关键流程的 mermaid 流程图:

graph LR;
    A[识别临界区] --> B[选择锁定方式];
    B --> C{是否为原子上下文};
    C -- 是 --> D[使用自旋锁并禁用中断];
    C -- 否 --> E[使用合适的锁];
    D --> F[执行临界区代码];
    E --> F;
    F --> G[释放锁];
    G --> H[检查是否有数据竞争];
    H -- 是 --> I[使用 KCSAN 检测];
    H -- 否 --> J[结束];
    I --> J;

通过这个流程图,可以清晰地看到内核同步的主要步骤,从识别临界区开始,到选择合适的锁定方式,执行临界区代码,释放锁,最后检查是否存在数据竞争。这个流程有助于开发者在实际开发中遵循正确的步骤,确保内核代码的正确性和稳定性。

相关推荐

告别锁总线!用PCIe原子操作在CXL/GPU加速场景下实现高性能数据同步

本文探讨了PCIe原子操作在CXL/GPU加速场景下的高性能数据同步应用。通过对比传统锁总线机制,详细介绍了PCIe原子操作的三种核心机制(FetchAdd、Swap、CAS)及其在异构计算系统中的优势,包括显著降低延迟、提升吞吐量。文章还提供了Linux内核中的实战配置指南和性能调优建议,帮助开发者实现高效的无锁同步方案。

weixin_42556197的博客 340

Delta Lake:数据湖的ACID临界点与事务层本质

数据湖架构长期面临可信性缺失、并发写入冲突、历史数据不可溯等核心挑战。Delta Lake并非新型存储格式,而是构建于Parquet之上的轻量级事务日志协议,将数据库级ACID、Schema强约束与Time Travel能力原生注入对象存储。其技术价值在于实现数据版本化、可测试、可回滚的工程化治理,支撑AI驱动的实时决策场景。典型应用场景包括银行级转账一致性保障、分钟级数据事故恢复、流批一体ETL自动化(Delta Live Tables)及变更数据捕获(CDF)。本文深入解析其日志协议设计、S3原子性实现

weixin_34295316的博客 458

AI编程CLI工具:开发者终端工作流的新内核

AI编程CLI工具是一种将大语言模型能力深度集成到命令行环境的工程化实践,其核心在于通过AST解析、管道化交互与本地化推理,实现代码生成、测试编写、重构优化等任务的自动化闭环。它不同于IDE插件或Web界面,强调可审计、可组合、可嵌入DevOps流水线的技术特性,适用于金融、政企等对合规性与数据可控性要求严苛的场景。随着Ollama、Tabby CLI、Claude Code CLI等工具成熟,AI编程CLI正成为继Git、Docker之后新一代基础开发设施,尤其在Python脚本生成、Shell自动化、跨

weixin_30780649的博客 390

深入解析ColdFire V1内核指令时序与中断控制器设计

在嵌入式系统开发中,指令执行时序和中断处理是保障系统实时性与确定性的核心基础。指令时序决定了CPU执行代码的效率,直接影响最坏情况执行时间(WCET)的估算,这是安全关键系统设计的基石。其原理涉及流水线、内存访问模型(包括对齐与非对齐访问)以及总线竞争,理解这些机制对于代码性能优化至关重要。中断控制器则是系统响应外部异步事件的“神经中枢”,它通过硬件优先级管理、向量表和嵌套机制,确保关键任务能得到及时处理。在实时嵌入式领域,如工业控制和汽车电子,精准掌控时序和中断能显著提升系统的可靠性和响应能力。本文聚焦于

weixin_34087307的博客 441

63、内核同步第一部分:关键概念与并发问题解析

本文深入解析了Linux内核同步的关键概念与并发问题,涵盖了原子性、临界区的定义与保护需求,详细介绍了数据竞争的正式定义及其检测工具LKMM与KCSAN。同时,文章探讨了Linux内核中并发问题的来源,并结合多核系统、可抢占内核、阻塞I/O以及硬件中断等场景分析了数据竞争的发生条件。此外,还介绍了常见的锁定技术,如自旋锁和互斥锁的使用准则,以及如何避免死锁和优化锁的使用。最后,讨论了内存顺序和内存屏障在多线程环境中的作用,并给出了内核同步的最佳实践建议。

dropout6artist的博客 66

63、内核同步第一部分:原子性、临界区与锁机制解析

本文深入解析了Linux内核同步机制,涵盖原子性、临界区的定义与保护、锁的使用与死锁问题,以及Linux内核内存模型(LKMM)对数据竞争的正式定义和判定。同时分析了多核SMP系统、可抢占内核、阻塞I/O和硬件中断等引发的并发问题,并提供了识别和保护临界区的实践指南。通过锁的使用准则和死锁检测工具,帮助开发者编写更健壮的内核代码。

m2n3b4v5c6的博客 83

63、内核同步(一):基础理论与关键概念解析

本文深入解析了Linux内核同步的基础理论与关键概念,包括原子性、关键部分的保护、数据竞争的定义与解决方案、自旋锁与互斥锁的使用场景及区别、内存屏障的作用以及KCSAN工具的使用。通过这些内容的学习,开发人员可以更好地应对内核并发问题,编写安全高效的内核代码。

4k5l6j7h8的博客 57

63、内核同步(上)

本文深入探讨了内核开发中的同步问题,涵盖原子性、临界区、数据竞争、锁的使用及死锁问题。详细介绍了自旋锁和互斥锁的适用场景及操作方法,并讨论了锁的性能优化策略。文章还总结了不同并发情况下的问题及解决方法,帮助开发者更好地应对内核开发中的并发挑战。

hp777的博客 46

【信息科学与工程学】【产品体系】计算机科学与自动化——第一百八十九篇 操作系统内核(微内核/宏内核)函数与算法06

编号类型领域内核类型(含版本)内核中的函数/算法/模块模块的调用关系模块/函数/算法逐步推理思考的数学表达式及参数列表及参数数值设计模块的 C/C++/其他代码关联知识D1​函数veth 发送宏内核(Linux 2.6.24+) ()上层协议栈调用 → → 获取 peer 设备 → 将 skb 转发到 peer 的 rx 队列 → 触发 NAPI 轮询参数:,(本端 veth)。 性能:每包约 200–400ns。 公式:。国产 OS 均支持;veth 总是成对出现。D2​函数veth 创建

weixin_49199313的博客 20

IEC61850建模实战:从标准解读到ICD文件生成

本文详细介绍了IEC61850建模的实战过程,从标准解读到ICD文件生成,涵盖了变电站自动化系统的结构化数据模型设计。通过分层结构、标准服务映射和XML文件解析,帮助读者掌握电力系统智能电子设备的标准化建模方法,提升工程实施效率。

weixin_30823001的博客 417

Pointnet++环境配置+复现

深度学习点云经典网络Pointnet++

weixin_58663121的博客 8075

Zemax序列模式下激光二极管(LD)光源建模与像散校正实战

本文详细讲解了在Zemax序列模式下精确建立激光二极管(LD)光源模型并校正其像散的核心方法。通过将椭圆光束的发散角转换为渐晕系数,并巧妙利用理想柱面镜模拟像散,实现了对LD光源特性的高精度模拟。该方法特别适用于激光耦合、激光雷达等需要对像散光束进行精密控制的光学系统设计。

weixin_28455801的博客 438

CentOS7下Node.js v20+安装与常见依赖库问题解决指南

本文详细介绍了在CentOS7系统下安装Node.js v20+的完整流程及常见依赖库问题的解决方案。通过升级glibc到2.28+、配置环境变量以及处理libstdc++版本问题,帮助开发者顺利在老旧系统上部署最新版Node.js,提升开发效率。

weixin_30689307的博客 157

你学Simulink--基于储能系统的建模与仿真场景:飞轮储能系统在电网调频中的作用仿真

本文详细介绍了利用Simulink+Simscape搭建飞轮储能系统(FESS)电网调频仿真模型的全过程。主要内容包括:系统工作原理及调频作用分析,基于Simulink的模块化建模步骤(飞轮、电机/发电机、功率电子接口等组件搭建),调频控制策略实现(频率检测、功率指令生成和模式切换),以及仿真参数设置与结果分析方法。该模型可有效模拟FESS在电网频率调节中的快速响应特性,为新能源存储和智能电网研究提供重要仿真工具,同时可通过参数优化和算法改进进一步提升系统性能。

xiaoheshang_123的博客 168

大模型微调部署实战及类GPT工具的高效使用

本文主要介绍了专栏《大模型微调部署实战及类GPT工具的高效使用》的核心内容,希望对使用大语言模型的同学们有所帮助。 文章目录 1. 前言 2. 专栏亮点 3. 你的收获 4. 详细目录

herosunly的博客 22万+

把 SAP Fiori Launchpad 配置文件放进 MIME Repository:从上传、缓存控制到可访问路径的完整实践

文章摘要: 在 SAP Fiori 项目中,Launchpad 配置文件的部署是关键挑战,核心在于通过 MIME Repository 而非直接修改标准文件来实现稳定配置。与 BSP Repository 相比,MIME Repository 支持显式设置客户端缓存时间(如 SAP 推荐的 7 天),适合生产环境治理。配置文件需为 JSON 格式,部署时需确保路径可访问(通过 ICF node 和 external alias),并注意语法错误会导致静默失效。实操步骤包括:在 SE80 中创建专用目录、设置

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 92

Camera2Demo

在Camera2 的基础上进行封装,使得Camera2一堆初始化逻辑与Activity分离,降低耦合度,完成预览拍照并保存的操作

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

上一篇: 30、数据库逻辑错误回闪恢复全解析
下一篇: 19、Web API、客户端集成与移动应用开发
grass
博客等级 码龄25年 1030粉丝 954原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值