“易失性”是否能保证多核系统的可移植 C 代码中的任何内容?

2023-12-24

看了一个之后 of https://stackoverflow.com/questions/78172/using-c-pthreads-do-shared-variables-need-to-be-volatile 问题 https://stackoverflow.com/questions/23449562/when-would-you-need-a-volatile-pointer and https://stackoverflow.com/questions/2478397/atomic-swap-in-gnu-c/2478520#2478520 their https://stackoverflow.com/a/44243426/706054 answers https://stackoverflow.com/a/2485733/706054,我的印象是对于 C 语言中“易失性”关键字的确切含义还没有达成广泛的共识。

就连标准本身似乎也不够明确,无法让大家达成共识这是什么意思 http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1956.htm.

其他问题包括:

  1. 它似乎提供不同的保证,具体取决于您的硬件和编译器。
  2. 它影响编译器优化,但不影响硬件优化,因此在自己进行运行时优化的高级处理器上,甚至不清楚编译器是否can阻止任何你想阻止的优化。 (一些编译器确实会生成指令来阻止某些系统上的某些硬件优化,但这似乎没有以任何方式标准化。)

总结一下这个问题,(在阅读了大量内容之后)“易失性”似乎保证了类似的内容:该值不仅会从寄存器读取/写入,而且至少会按照代码中出现的读取/写入顺序读取到内核的 L1 缓存。但这似乎毫无用处,因为在同一线程中从寄存器读取/写入寄存器已经足够了,而与 L1 缓存协调并不能保证与其他线程的协调有任何进一步的保证。我无法想象什么时候只与 L1 缓存同步变得很重要。

USE 1
唯一得到广泛认可的 volatile 似乎是用于旧的或嵌入式系统,其中某些内存位置被硬件映射到 I/O 功能,例如内存中控制(直接在硬件中)灯的位,或者内存中的一个位可以告诉您键盘按键是否按下(因为它是由硬件直接连接到按键的)。

看起来“use 1”不会出现在目标包括多核系统的可移植代码中。

USE 2
与“use 1”没有太大区别的是可以由中断处理程序随时读取或写入的内存(可以控制灯或存储来自按键的信息)。但为此我们已经遇到了一个问题,根据系统的不同,中断处理程序可能会运行 https://stackoverflow.com/q/33955582/706054 不同的核心 https://stackoverflow.com/a/25153972/706054 with 它自己的内存缓存 https://stackoverflow.com/a/954069/706054,并且“易失性”并不能保证所有系统上的缓存一致性。

So “use 2”似乎超出了“volatile”所能提供的范围。

USE 3
我看到的唯一其他无可争议的用途是通过指向同一内存的不同变量来防止访问错误优化,而编译器没有意识到这是同一内存。但这可能是无可争议的,因为人们没有谈论它——我只看到一次提到它。我认为 C 标准已经认识到“不同”指针(如函数的不同参数)可能指向相同的项目或附近的项目,并且已经指定编译器必须生成即使在这种情况下也能工作的代码。然而,我无法在最新的(500 页!)标准中快速找到这个主题。

So “use 3”可能不存在 at all?

因此我的问题是:

“易失性”是否能保证多核系统的可移植 C 代码中的任何内容?


编辑——更新

浏览完后最新标准 https://web.archive.org/web/20181230041359if_/http://www.open-std.org/jtc1/sc22/wg14/www/abq/c17_updated_proposed_fdis.pdf,看起来答案至少是very有限的是:
1、标准反复规定了对特定类型“volatile sig_atomic_t”的特殊处理。然而,该标准还指出,在多线程程序中使用信号函数会导致未定义的行为。因此,这个用例似乎仅限于单线程程序及其信号处理程序之间的通信。
2. 该标准还规定了与 setjmp/longjmp 相关的“易失性”的明确含义。 (重要的示例代码在其他中给出问题 https://stackoverflow.com/questions/1393443/setjmp-longjmp-and-local-variables and answers https://stackoverflow.com/questions/7996825/why-volatile-works-for-setjmp-longjmp.)

所以更精确的问题变成:
除了 (1) 允许单线程程序从其信号处理程序接收信息,或 (2) 允许 setjmp 代码查看 setjmp 之间修改的变量之外,“易失性”是否能保证多核系统的可移植 C 代码中的任何内容还有longjmp?

这仍然是一个是/否问题。

如果“是”,如果您能展示一个无错误的可移植代码的示例,如果省略“易失性”,该代码就会出现错误,那就太好了。如果“否”,那么我认为对于多核目标,编译器可以自由地忽略这两种非常具体的情况之外的“易失性”。


我不是专家,但 cppreference.com 有一些在我看来相当不错的东西有关的信息volatile https://en.cppreference.com/w/c/language/volatile。这是它的要点:

通过左值表达式进行的每次访问(读和写) 挥发性限定类型被认为是可观察到的副作用 优化的目的并严格按照 抽象机的规则(即所有写入都在 下一个序列点之前的某个时间)。这意味着在一个 单线程执行,无法优化易失性访问 或相对于分离的另一个可见副作用重新排序 通过来自易失性访问的序列点。

它还提供了一些用途:

挥发性的用途

1)静态易失性对象模型内存映射I/O端口,以及静态 const 易失性对象模型内存映射输入端口,例如 实时时钟

2) sig_atomic_t 类型的静态易失性对象用于 与信号处理程序的通信。

3) 易失性变量是包含以下内容的函数的局部变量 setjmp 宏的调用是唯一保证的局部变量 在 longjmp 返回后保留它们的值。

4)此外,易失性变量可用于禁用某些 优化的形式,例如禁用死存储消除或 微基准的不断折叠。

当然,它提到了volatile对于线程同步没有用:

注意,易失性变量不适合通信 线程之间;它们不提供原子性、同步性或 内存排序。读取由以下命令修改的易失性变量 另一个没有同步或并发修改的线程 由于数据竞争,两个不同步的线程是未定义的行为。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

“易失性”是否能保证多核系统的可移植 C 代码中的任何内容? 的相关文章

随机推荐