【问题标题】:Can mutex replace memory barriers互斥锁可以代替内存屏障吗
【发布时间】:2015-04-28 13:39:11
【问题描述】:

我试图了解内存障碍,但偶然发现了以下维基百科链接 http://en.wikipedia.org/wiki/Memory_barrier 这很好地解释了这个概念,但如果这在我们有 mutex() 锁定内存部分的系统中真的很有帮助的话。

采用与维基百科中提到的相同的代码,下面的方法是否可以解决使用互斥锁的问题?

[注意:函数名称不特定于任何编程语言,只是为了简单起见]

处理器 #1

mutex_lock(a)
while (f == 0);
print x;
mutex_unlock(a)

处理器 #2

mutex_lock(a)
x = 42;
f = 1;
mutex_unlock(a)

【问题讨论】:

  • 如果处理器 #1 先获取锁,该示例将死锁,因为此时处理器 #1 将永远旋转,而处理器 #2 在尝试获取锁时会卡住。

标签: memory linux-kernel mutex memory-barriers


【解决方案1】:

内存屏障保证先前指令的所有可见效果在任何后续指令的效果之前变得可观察。可能会重新排列观察到的效果顺序的事情是:

  • 编译器(通过重新排序指令)
  • 乱序管道
  • 具有宽松内存一致性的缓存系统(几乎都在现代系统上)

互斥锁保证一次只有一个线程持有该互斥锁。

这两个概念之间存在关系:如果没有至少部分内存屏障,互斥体实际上是无用的。考虑这个例子:

mutex_lock(a);
x = x+1;
mutex_unlock(a);

锁操作必须至少有一个“获取”屏障,以防止在获取锁之前出现 x 的加载。同样,“解锁”操作必须至少有一个“释放”屏障,以防止 x 的存储在锁被释放后出现。那就是锁定-解锁对形成一个笼子,操作无法逃脱。 (虽然有时程序员会在重新排序导致操作爬进笼子时感到惊讶!)

因此,锁定互斥体并立即解锁它就像一个内存屏障,尽管它的效率非常低,因为它会强制串行执行。

【讨论】:

    【解决方案2】:

    内存条通常用于使无序的管道进入关于其内存访问的明确定义的状态,因此与互斥锁的概念正交,互斥锁通常是更高级别的多处理中的概念(与 CPU 指令的乱序执行无关)。

    【讨论】:

      【解决方案3】:

      内核中的互斥锁和其他锁在内部使用屏障来确保代码按预期的确切顺序运行。 在编译器中使用优化时,您永远不应假设指令将按照与 源代码。编译器可能会以优化寄存器使用方式的方式对汇编语言指令进行重新排序。此外,现代 CPU 通常并行执行多条指令,并且可能会重新排序内存访问。这些类型的重新排序可以大大加快程序的速度。 但可能会导致意外输出!

      因此,MEMORY BARRIER 原语确保与放置在原语之前的 C 语句相对应的汇编语言指令不会被编译器与对应于放置在原语之后的 C 语句的汇编语言指令混合。在 linux barrier() 宏是:

      asm volatile("":::"memory")
      

      解释如下:

      1. asm 指令告诉编译器插入一个汇编语言片段
      2. volatile 关键字禁止编译器将 asm 指令与程序的其他指令重新洗牌
      3. memory 关键字强制编译器假定 RAM 中的所有内存位置都已被汇编语言指令更改;因此,编译器无法通过使用 asm 指令之前存储在 CPU 寄存器中的内存位置的值来优化代码

      【讨论】:

      • 将 C 语言中的内联汇编的使用与 Linux 编程混为一谈并不是一个好主意。
      猜你喜欢
      • 2014-09-11
      • 2016-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多