内存访问顺序 - part2: 屏障及Linux kernel中屏障的使用

7 篇文章 0 订阅


本文翻译自 Memory access ordering part 2: Barriers and the Linux kernel.
该系列有 3 篇文章,其余两篇是:

作者 Leif Lindholm 在 ARM 工作多年,经验丰富,虽然这篇文章发表于 2013 年,但是还是很有借鉴价值的。


前一篇文章 介绍了内存访问顺序的概念。但是它没有提供任何解决问题的办法,也没有具体说明这种排序的重要性。
当今,并非所有的软件开发者都需要深入了解内存访问顺序或屏障。除非您的代码直接与硬件打交道,或者直接与在其他内核上执行的代码打交道,或者直接加载或生成要执行的指令,否则大多数情况下您的代码都会正常工作。

  • 如果您与硬件的交互完全是通过设备驱动程序进行的(即没有设备控制器直接映射到您的应用程序中),那么设备驱动程序有责任保证执行顺序。
  • 如果您与运行在不同内核上的软件的通信使用到了多线程API,例如使用了PthreadsJava 线程,那么该API有责任保证执行顺序。
  • 如果您的程序在具备分页功能的操作系统上执行,那么操作系统有责任保证执行顺序。
    但是,如果您正在编写设备驱动程序,或实现自己的线程通信,或创建 JIT 编译器,则如果不了解屏障barrier)的正确使用可能会导致意外的难以诊断的问题。如果您的程序要求特定的内存访问顺序才能被系统中的多个内核或设备看到,那么该解决方案称为屏障。
    尽管底层的体系结构概念本身很有趣,但它们并不是大多数与屏障相关的软件开发人员所需要知道的。因此,本文仅涉及Linux内核中的屏障使用。我保证在以后的文章中再讲详细的细节。

屏障是什么

屏障barrier)在某些体系结构中称为栅栏(fence),是一种显示地执行某种类型的内存访问顺序的操作。
从较高的层次上来讲,这可能意味着编译器指令防止加载/存储操作在源代码中跨行重新排序,但是允许编译器对任意一侧的内存访问或同一侧的其他访问进行重新排序。从较低的层次上来讲,这可能意味着使用专用指令停止内核的运行,直至确保所有先前的内存访问对系统中的其他代理可见。代理是系统中能够启动总线事务的任何设备——例如处理器或DMA控制器。
图1是屏障影响加载-存储(load-store)指令顺序的例子。
在这里插入图片描述

图中Load2依赖Store1。例如,Store1可能是对配置存储器的写操作,其需要重新映射外设的物理地址,然后由Load2读取。注意,在没有地址依赖关系的情况下,屏障两侧的访问仍然可以自由地重新排序。

Linux Kernel 中的屏障

由于供应商,体系结构和整个系统组件整体之间的编译器指令,屏障指令和其他系统操作会有所不同,因此Linux内核定义了一组需要为每个体系结构实现的可移植的屏障操作集。由于受支持的具有最弱内存模型的体系结构(实际上是允许最多重新排序的模型)是DEC Alpha,因此将其用做参考体系结构。在这方面,没有其他体系结构可以超越DEC Alpha,但是ARMv7-A非常接近。Linux内核中可用屏障的完整文档在Linux/Documentation/memory-barrier.txt,这里只做一个简短的介绍。

Linux 屏障 API

一般的屏障

一般的屏障对运行时没有影响,它只是编译器的一条指令,以防止出于优化目的而对内存访问进行重排序。

方法说明
barrier()编译器级别的屏障。编译器不会将内存访问从该语句的一侧重新排序到另一侧。这对处理器实际执行生成的指令的顺序没有影响。

volatile 关键字也可以做到每次都从内存中重新读取变量的值,而不是用寄存器中暂存的值。
Linux内核中编译器级别的屏障(compiler barrier)是 barrier(),定义在 include/linux/compiler-gcc.h

/* The "volatile" is due to gcc bugs */
#define barrier() __asm__ __volatile__("": : :"memory")

这里的 memory 是告诉编译器,代码修改了内存中的内容,需要重新从内存中读取数据,而不是使用寄存器中暂存的数据。这条语句只会对编译器的行为产生约束,不会生成真正的指令,因此对CPU最终的指令执行没有任何影响。

强制性屏障

强制性屏障用于强制整个系统级别的内存一致性。常用于与映射的外设进行通信时。无论是什么体系结构,所有的强制性屏障都保证至少可以扩展到编译器级别的屏障。

方法说明
mb()完整的系统内存屏障。mb()之前的所有内存操作都执行完,才能执行mb()之后的操作。这个顺序对系统中所有的总线主控都是可见的。它还确保从单个处理器到从属设备的访问顺序。
rmb()mb()类似,但只保证读操作的访问顺序,也就是说,rmb()之前所有的读操作将在rmb()之后的任何读操作之前完成。
wmb()mb()类似,但只保证写操作的访问顺序,也就是说,wmb()之前所有的写操作将在wmb()之后的任何写操作之前完成。

Linux内核中 mb()wmb(), rwb()的定义如下:

#define mb()		dsb(sy)
#define rmb()		dsb(ld)
#define wmb()	    dsb(st)
#define dsb(opt)	asm volatile("dsb " #opt : : : "memory")

可见用的都是 dsb 指令,sy 的意思是 systemld 的意思是loadst的意思是store

SMP 条件屏障

SMP条件屏障用于保证SMP系统中不同核之间内存视图的一致性。当编译一个没有配置CONFIG_SMP的Linux内核时,所有的SMP屏障都被转换成普通的编译器屏障。
注意:SMP屏障是强制性屏障的子集,不是超集(这是一个常见的误解)。SMP屏障不能取代强制性屏障,但是强制性屏障可以取代SMP屏障。

方法说明
smp_mb()mb()类似,但是只能保证SMP系统中内核/处理器之间的顺序。smp_mb()之前所有的内存访问对SMP系统中所有的内核都是可见的。
smp_rmb()smp_mb()类似,但只保证读操作的访问顺序。
smp_wmb()smp_mb()类似,但只保证写操作的访问顺序。
#define __smp_mb()	dmb(ish)    // ish: inner-shareable
#define __smp_rmb()	__smp_mb()
#define __smp_wmb()	dmb(ishst)  // ishst: inner-shareable, store
#define dmb(opt)	asm volatile("dmb " #opt : : : "memory")

隐式屏障

Linux内核中使用锁结构来充当隐式SMP屏障,与用户空间的pthread同步操作相同。当使用这些来保护共享资源时,就不必使用显式屏障(以确保该资源的一致性)。但是,这并不能消除在与外部主机通信时对显式屏障的需要。
由于大量的设备驱动程序没有使用所需的屏障,因此当Linux内核使用CONFIG_ARM_DMA_BUFFERABLE时,ARM体系结构的I/O访问器宏(readb(), iowrite32()等)将充当显式的内存屏障。这是在Linux-2.6.35中增加的。

其他屏障

在 Linux 内核中还存在其他屏障,本文只涵盖了最常见的几种屏障。更多的信息请参考Linux内核文档。

屏障的开销

使用屏障的真正原因是为了防止编译器和硬件做了不安全的优化。此外,存在不同类型的屏障以便准确地描述需要强制执行的内存顺序。这意味着屏障会对系统性能产生负面影响。花费额外的时间来确定特定的情况下是否需要屏障,如果需要屏障,应该用哪个屏障,是非常值得的。

未来的文章

我的下一篇文章将介绍ARM体系结构中可用的屏障和操作。

  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
IEC 60601-2-64是一项国际标准,适用于医疗电气设备,其全称为“医疗电气设备第2-64部分:特殊要求”。该标准主要关注用于麻醉、通气和监测的呼吸支持设备。 IEC 60601-2-64为这些呼吸支持设备制定了特定的要求,以确保其安全性和有效性。该标准涵盖了麻醉机、呼吸机、机械通气设备、连续气道正压道具和相应附件等设备。这些设备在医疗环境具有关键作用,对病患的生命支持和疾病治疗起着重要作用。 根据IEC 60601-2-64标准,这些呼吸支持设备需要符合一系列的技术要求和性能指标。其包括对电气安全、机械安全、防火安全、电磁兼容性和生物兼容性的要求。此外,该标准还对设备的设计、材料、运行和维护提出了详细的要求,以确保其在正常操作和异常情况下的稳定性和可靠性。 IEC 60601-2-64还强调了设备的人体工程学设计,以确保其符合使用者的人体特征和操作习惯。此外,标准还对设备的性能测试和验证提出了具体的要求,以保证其在实际使用的可靠性和安全性。 通过遵循IEC 60601-2-64标准,制造商可以确保其生产的呼吸支持设备达到国际安全标准,并提供给医疗机构和专业人员安全可靠的设备,以提供高质量的医疗服务。同时,该标准也有助于确保病患在接受治疗期间获得最佳的呼吸支持和监测效果,最大程度地保护病患的生命安全和健康。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值