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

mb与使用场景

它是 Linux 内核等领域的一个底层原语。
mb()内存屏障 或更具体地说是 内存隔离屏障 的一种。它的使用场景深刻关系到现代计算机体系的乱序执行多核并发。换句话说,如果不是编译器的优化,把c语句进行可能的调整,导致执行次序和代码前后关系不一致,也不需要这个patch式功能;比如c语句 a-b-c三行,三者没有逻辑依赖,只是先后次序,编译器可能会优化为a-c-b;

核心问题:为什么需要内存屏障?

现代 CPU 和编译器为了提升性能,会对指令执行顺序内存访问顺序进行优化,这可能导致指令不按代码的先后顺序执行。主要在两个层面发生:

  1. 编译器优化:编译器在编译时,可能会将指令重新排序,以提高指令流水线的效率。
  2. CPU 乱序执行:CPU 在执行时,如果后续指令不依赖于前面指令的结果,它可能会让后续指令先执行。此外,由于多级缓存的存在,对不同内存地址的写入操作,在其他 CPU 核心看来,其全局可见的顺序可能与写入顺序不一致。

在单核、单线程程序中,这种乱序通常是透明且无害的,因为最终结果是一致的。但在多核并发环境(特别是内核中)下,这种乱序会导致严重问题。


mb() 函数的使用场景

mb()(全称 smp_mb(), 即 Symmetric Multi-Processor Memory Barrier)的作用是创建一个完整的 memory barrier。它确保:

  • 屏障之前的所有内存访问操作(读和写),一定在屏障之后的所有内存访问操作之前完成。
  • 这个“完成”意味着结果已经对系统中的所有 CPU 核心可见。

它就像一个栅栏,阻止屏障两侧的指令穿越它进行重排序

以下是几个经典的使用场景:

场景一:实现锁机制

锁是内存屏障最根本的应用场景之一。

static int locked = 0; // 0: unlocked, 1: lockedvoid acquire_lock(void) {while (test_and_set(&locked) == 1) { // 尝试原子性地获取锁/* 等待 */}mb(); // 内存屏障!确保在拿到锁之后,临界区的读写操作不会跑到拿锁之前去。
}void release_lock(void) {mb(); // 内存屏障!确保在释放锁之前,临界区内的所有读写操作都已经完成。locked = 0;
}
  • acquire_lockmb() 确保一旦成功获取锁,后续进入临界区的代码不会因为乱序执行而被提前到获取锁之前,从而避免了数据竞争。
  • release_lockmb() 确保在释放锁(让其他线程可见)之前,临界区内的所有修改都已经完成并全局可见。否则,另一个线程可能看到旧的、不一致的数据。
场景二:生产者-消费者模式(使用共享标志位)

这是最经典、最能说明问题的场景。

// 共享数据
struct data {int value;int ready; // 标志位:0-数据未就绪,1-数据已就绪
};// 生产者
void producer(struct data *data) {data->value = 42;          // 1. 准备数据mb();                     // 2. 内存屏障!确保 value=42 的写入一定在 ready=1 之前对消费者可见。data->ready = 1;           // 3. 发布数据,标记为就绪
}// 消费者
void consumer(struct data *data) {while (data->ready == 0) { // A. 等待数据就绪/* 循环 */}mb();                     // B. 内存屏障!确保在读取 value 之前,一定已经看到了 ready=1 这个结果。int used_value = data->value; // C. 使用数据
}

如果没有内存屏障会发生什么?

由于乱序执行,CPU 或编译器可能让生产者的 data->ready = 1 先于 data->value = 42 执行并变得全局可见。这时,消费者线程看到 ready 变为 1,便跳出循环去读取 value,但此时 value 可能还是旧的(未初始化的)值,导致程序错误。

内存屏障的作用:

  • 生产者的 mb() 保证了 1 -> 2 -> 3 的顺序,消费者绝不会看到一个 ready=1value 却不正确的状态。
  • 消费者的 mb() 保证了 A -> B -> C 的顺序,确保在读取 value 之前,已经正确地感知到了 ready 的变化。
场景三:与设备交互(DMA)

当内核与硬件设备通过 DMA 直接访问内存时,顺序至关重要。

// 1. 告诉设备要读取的数据在哪里(写入设备寄存器)
writel(DMA_SOURCE_ADDR_REG, buffer_phys_addr);
writel(DMA_SIZE_REG, size);mb(); // 内存屏障!确保上面两条配置信息一定先于下面的启动命令被设备看到。// 2. 启动 DMA 读取操作
writel(DMA_START_REG, 1);

如果没有屏障,设备可能会先收到 DMA_START 命令,然后才收到地址和大小,这会导致设备行为异常。


其他类型的内存屏障

Linux 内核提供了不同强度和作用范围的屏障,mb() 是其中最严格的。

  1. 写屏障 wmb():只确保写操作的顺序。屏障前的写操作一定在屏障后的写操作之前完成。
  2. 读屏障 rmb():只确保读操作的顺序。屏障前的读操作一定在屏障后的读操作之前完成。
  3. 编译器屏障 barrier():仅阻止编译器重排序,不生成任何 CPU 指令。适用于防止编译器优化但不需要干预 CPU 乱序的场景(例如,在循环忙等待中)。

总结

在任何一个需要明确指定内存操作顺序,否则并发逻辑就会出错的地方,就是你该考虑使用内存屏障(如 mb())的时候。它是编写正确、可靠的多核并发底层代码的基石。
mb() 内存屏障的使用场景可以归结为:

场景特征具体例子屏障的作用
存在共享数据的多核/多线程环境锁、生产者-消费者确保修改顺序可见性顺序的一致性,防止数据竞争和状态不一致。
对访问顺序有严格要求的硬件交互设备驱动、DMA确保CPU对设备寄存器的写入顺序符合硬件的工作逻辑。
需要保证指令执行顺序各种同步原语的实现作为一个“栅栏”,强制在屏障点完成所有之前的操作,再开始之后的操作。
http://www.dtcms.com/a/499753.html

相关文章:

  • 建设通网站是什么时间成立加入google广告wordpress
  • AI Coding 基础实践01 - TickTalk的MarsCode-Trae AI(Trae 插件)在Pycharm中的配置
  • [SCADE编译原理] 因果性分析原理(2001)
  • 网站建设pc指什么软件佛山新网站建设策划
  • RDEx:一种效果驱动的混合单目标优化器,自适应选择与融合多种算子与策略
  • JavaScript学习第三天:运算符
  • C++进阶之操作符重载函数operator[]:用法实例(四百三十五)
  • 《小白学随机过程》第一章:随机过程——定义和形式(附录2. 随机变量和随机过程公式解读)
  • 近代通信技术的发展
  • 实用网站的设计与实现wordpress简介
  • 如何微信做演讲视频网站Wordpress刷新CDN缓存
  • macos虚拟机-演示篇一制作可启动iso文件
  • 论坛类网站备案今天东营发生的重大新闻
  • Aspect的AOP实现
  • Orleans Stream SubscriptionId 生成机制详解
  • FMIT,一款专业的乐器调音助手
  • 医疗器械招商网站大全建一个信息 类网站
  • 不用域名推广网站开源网站后台管理系统
  • 欧司朗与日亚签署广泛的知识产权协议
  • Kotlin 与 Java 互操作中常用注解
  • 计算机操作系统:程序的装入与链接
  • 怎么建设网站手机网站制作价格在线考试类网站怎么做
  • 3.1 栈
  • 国贸网站建设公司服务专业的网站建站公司
  • 手写观察者模式:原理、实现与应用
  • 商贸有限公司网站案例wordpress模版安装
  • Rclone、rsync、Docker 的 COPY/ADD:路径末尾加不加「/」的含义大不相同!
  • 【复习】计网每日一题1016--可分配
  • 【STM32】hal库 多通道ADC(+DMA)采集 连续模式
  • 网站收录量是什么意思开网站卖茶要怎么做