当前位置: 首页 > news >正文

Linux 内存管理章节十四:多核世界的交通规则:深入Linux内存屏障与并发控制

引言

在现代多处理器系统中,每个CPU核心都有自己的缓存。当一个CPU修改内存时,这个修改可能首先存在于其本地缓存中,稍后才传播到主内存和其他CPU的缓存。这种异步性带来了巨大的性能提升,但也引入了一个核心挑战:从不同CPU的视角看去,内存操作的顺序和可见性可能是不一致的。如果没有明确的同步,一个CPU可能看不到另一个CPU刚刚写入的值,或者看到操作的顺序与程序代码的顺序不符。Linux内核通过内存屏障(Memory Barriers) 和诸如RCU等高级并发原语来应对这一挑战,它们就像是多核世界中的“交通规则”和“交通管制系统”。

一、 内存一致性模型:架构的承诺

首先,必须理解硬件提供的基础保证。不同的CPU架构提供了不同的内存一致性模型(Memory Consistency Model),它定义了内存操作在多个处理器视角下的可见性规则。

两种主要模型:

  1. 顺序一致性(Sequential Consistency)

    • 最直观的模型:所有CPU核心看到的任何内存操作的顺序都是一致的,并且与程序顺序(Program Order)一致。
    • 性能差:要求严格的全局顺序,严重限制了硬件优化,现代CPU几乎都不采用
  2. 弱一致性(Weak Consistency)或松弛一致性(Relaxed Consistency)

    • 现代CPU的默认模型(如x86、ARM、PowerPC)。为了性能,硬件允许:
      • 乱序执行(Out-of-Order Execution):CPU可能为了效率而打乱没有数据依赖的指令的执行顺序。
      • 写缓冲(Store Buffers):写入操作可能先进入一个缓冲区,稍后才被提交到缓存/内存。
    • 这意味着:一个CPU的写入操作可能无法立即被其他CPU看到,并且不同CPU观察到的操作顺序可能不一致。

因此,程序员不能依赖默认的硬件行为来保证并发正确性。必须显式地使用内存屏障来强制实现所需的顺序和可见性。

二、 各种内存屏障的作用和使用场景:指挥交通

内存屏障是一条特殊的CPU指令,它在其所在的位置设立一个“栅栏”,对屏障前后的内存操作进行排序。Linux内核提供了一系列宏来封装不同架构的屏障指令。

1. 编译器屏障
  • 作用:仅阻止编译器为了优化而重排屏障前后的指令。它不产生任何CPU指令,不影响CPU的乱序执行。
  • APIbarrier()
  • 使用场景:防止编译器优化破坏一些隐式的硬件内存顺序依赖。例如,在读写内存映射的设备寄存器时。
2. 硬件内存屏障

硬件屏障根据其约束的操作类型(读/写)和方向(之前/之后)进行分类。

屏障类型API (通用)作用典型使用场景
通用内存屏障mb()双向屏障。确保屏障前的所有读/写操作完成后,才开始屏障后的任何读/写操作。在更新数据结构后发布指针,确保新数据对他人可见后再发布指针。
写内存屏障wmb()写操作屏障。仅确保屏障前的所有操作完成后,才开始屏障后的操作。不约束读操作在更新环形队列(Ring Buffer)时,先写入数据,然后wmb(),最后更新“写指针”(索引)。确保数据先于指针可见。
读内存屏障rmb()读操作屏障。确保屏障前的所有操作完成后,才开始屏障后的操作。不约束写操作从环形队列读取时,先读“写指针”,然后rmb(),最后读数据。确保先获取有效的指针,再读取该指针所指向的数据。
数据依赖屏障dma_rmb()弱于读屏障。仅保证有数据依赖关系的读操作的顺序。例如,p = ptr; data = *p;,它保证加载ptr在加载*p之前完成。在无锁编程和DMA场景中非常高效,因为它比完整的rmb()开销更小。

一个经典例子:SPSC(单生产者单消费者)环形队列

  • 生产者
    1. 将数据写入队列的slot[]
    2. wmb(); // 写屏障:确保数据写入一定在更新索引之前对消费者可见
    3. 更新write_index
  • 消费者
    1. 读取write_index
    2. rmb(); // 读屏障:确保读取索引一定在读取数据之前
    3. slot[]中读取数据。

如果没有这些屏障,消费者可能会看到新的write_index但却读到旧的、未更新的数据。

三、 RCU在内存管理中的应用:读多写少的利器

RCU(Read-Copy-Update)是一种高级的同步机制,其核心优势是:在读者路径上完全无锁,性能极高,且不会导致读者饿死。它非常适合“读多写少”的场景,而这在内存管理中非常常见。

RCU基本原理:
  1. 读侧:读者通过rcu_read_lock()rcu_read_unlock()标记一个临界区。在该区域内,读者可以安全地引用RCU保护的数据结构,但不能阻塞
  2. 写侧
    • Copy:写者想要更新数据时,不是直接修改,而是先创建一份数据的副本
    • Update:修改这份副本。
    • 替换:使用一个原子操作(如指针赋值)将指向旧数据的指针替换为指向新数据的指针。这是发布(Publish) 的时刻。
    • 宽限期(Grace Period):写者需要等待一个“宽限期”,确保所有在替换操作之前进入读临界区的读者都已经退出。这意味着这些读者肯定已经看到了旧数据,而不会再持有旧数据的引用。
    • 回收:宽限期结束后,写者可以安全地释放旧数据
在内存管理中的应用:
  1. 维护内存区域VMA的红黑树

    • 进程的VMA树经常被page fault处理路径(读者)遍历,但相对较少被mmapmunmap(写者)修改。
    • 使用RCU来保护对VMA树的遍历,可以极大地提升并发性能,避免读者因写者而阻塞。
  2. 反向映射(rmap)的遍历

    • 操作一个物理页的所有PTE(通过反向映射链表)是一个频繁的读操作(如页面迁移、回收)。
    • 而修改这个链表(如添加/删除PTE)则相对较少。
    • 使用RCU来保护反向映射链表的遍历,可以避免在扫描所有映射时持有大量的锁,减少了锁竞争,提升了页面回收等操作的效率。
  3. SLAB分配器缓存列表

    • 内核中维护了多个SLAB缓存列表。读取这个列表是常见的(如/proc/slabinfo),而修改(创建/销毁缓存)则很罕见。
    • 使用RCU保护这些列表的访问是理想的选择。

RCU的本质:它是一种基于发布/订阅和延迟回收的同步机制。它通过推迟销毁来消除读端的同步开销,用空间(多一份副本)和写端的延迟来换取读端的极致性能。

总结

内存屏障和RCU是Linux内核应对多核并发挑战的核心工具:

  • 内存屏障是底层的、精确的“交通指挥”,通过在关键位置强制排序,确保内存操作的可见性和顺序性符合程序员的预期。理解wmb()rmb()的成对使用是编写正确并发代码的关键。
  • RCU是一种更高层次的同步抽象,它巧妙地利用写时复制(Copy)延迟回收(Reclaim),为“读多写少”的场景提供了近乎无锁的读性能,在内存管理的多个核心数据结构中发挥着重要作用。

掌握这些概念,意味着你从一名被动的内存使用者,转变为一名能够主动、安全、高效地驾驭多核系统内存资源的资深内核开发者。这是编写高性能、高可靠性内核代码的必经之路。


文章转载自:

http://8aIThxMS.ppdsL.cn
http://vbfJccO8.ppdsL.cn
http://RF5JEnxl.ppdsL.cn
http://0bdO4wMg.ppdsL.cn
http://SBmAdX2I.ppdsL.cn
http://TJIJ2CGf.ppdsL.cn
http://GclNpndd.ppdsL.cn
http://aWPhEisQ.ppdsL.cn
http://9dh5ufKp.ppdsL.cn
http://QIeE9WJh.ppdsL.cn
http://nKBlBMG4.ppdsL.cn
http://qxyQdmam.ppdsL.cn
http://l84REzSD.ppdsL.cn
http://gc2skxIA.ppdsL.cn
http://ZkyAQgu1.ppdsL.cn
http://ao2utbqy.ppdsL.cn
http://1SA45iR9.ppdsL.cn
http://RrlVy717.ppdsL.cn
http://r3Cb9Mo1.ppdsL.cn
http://mvZ0osg4.ppdsL.cn
http://r8HpLyuE.ppdsL.cn
http://uhuOpdj9.ppdsL.cn
http://3Y9Qujbx.ppdsL.cn
http://U7K0Iu8O.ppdsL.cn
http://QdDgJbSL.ppdsL.cn
http://ItnOimVI.ppdsL.cn
http://ccnQhcH2.ppdsL.cn
http://3h4EKnwn.ppdsL.cn
http://691OB7Oq.ppdsL.cn
http://NJsse5Dz.ppdsL.cn
http://www.dtcms.com/a/386539.html

相关文章:

  • .NET Core 中生成 JWT(JSON Web Token)
  • webRTc 为何深受直播实现的青睐?
  • iOS App 卡顿与性能瓶颈排查实战 如何定位CPU内存GPU帧率问题、优化耗电与网络延迟(uni-app开发性能优化全流程指南)
  • Tomcat的基本配置
  • Delphi6中实现PDF文件打印功能
  • 工作笔记-----基于FreeRTOS的lwIP网络任接收过程,从MAC至协议栈
  • ZipVoice小米语音合成-MacOS可运行
  • 技术驱动学术论文写作创新:以智能工具高效生成论文提纲为例
  • (笔记)进程间通讯
  • 电力行业数字化——解读麦肯锡企业数据架构数据治理架构设计规划【附全文阅读】
  • 如何搭建redis集群(docker方式非哨兵)
  • AWS Free Tier 2.0深度技术解析与实战指南
  • 深度学习-PyTorch基本使用
  • 飞书智能查询机器人搭建说明文档
  • 速通ACM省铜第六天 赋源码(MEX Count)
  • Python自动化测试·Selenium简单介绍
  • 腾讯云轻量服务器CentOSdocker报错信息
  • 玩转Docker小游戏项目系列: Docker部署红心纸牌网页小游戏
  • Spring Cloud 注册中心:Eureka 与 Nacos 深度对比
  • 机器视觉检测中光源的作用以及分类
  • php7.4使用systemd服务器管理文件无法启动的解决办法
  • 机器视觉检测中工业相机的作用以及分类
  • MySQL 备份与还原
  • 5 分钟 SAE 极速部署 Dify,高效开发 AI 智能体应用
  • [硬件电路-233]:增强型MOS中的增强,是指通过增加正电压使得沟道从无到有的增强。耗尽型MOS中的耗尽,通过增加负电压使得沟通从最大逐渐减弱直到耗尽。
  • 整体设计 之 绪 思维导图引擎 之 引 认知系统 之 引 认知系统 之 序 认知元架构 之 概要设计收官 之1 汇总 形式化表示
  • TDengine 时序函数 DIFF 用户手册
  • 60.[前端开发-Vue3]Day02-模板语法-列表渲染-OptionsAPI-侦听器
  • UML_类图
  • 八串口服务器-工业物联网解决方案