挥发性及其有害影响

2023-11-25

我是一名嵌入式开发人员,在使用 I/O 端口时使用 volatile 关键字。但我的项目经理建议使用 volatile 关键字是有害的,并且有很多缺点,但我发现在大多数情况下 volatile 在嵌入式编程中很有用,据我所知,挥发性在内核代码中是有害的,因为对我们的代码的更改将变得不可预料的。在嵌入式系统中使用 volatile 也有什么缺点吗?


No, volatile是无害的。在任何情况下。曾经。没有可能的格式良好的代码会因添加以下内容而中断volatile到一个对象(以及指向该对象的指针)。然而,volatile常常不被理解。内核文档指出的原因volatile被认为有害的是人们继续以破坏的方式使用它来实现内核线程之间的同步。特别是,他们使用volatile整数变量就好像对它们的访问保证是原子的,但事实并非如此。

volatile也并非毫无用处,特别是如果你使用裸机,你will需要它。但是,与任何其他工具一样,理解其语义也很重要volatile在使用它之前。

What volatile is

进入volatile在标准中,对象被认为是副作用与增加或减少相同的方式++ and --。特别是,这意味着 5.1.2.3 (3),其中表示

(...)如果实际实现可以推断出其值未被使用并且不会产生所需的副作用(包括由调用函数或访问易失性对象引起的任何副作用),则实际实现不需要评估表达式的一部分

不适用。编译器必须丢弃它认为知道的有关 a 值的所有内容volatile每个序列点都有变量。 (与其他副作用一样,当访问volatile对象发生由序列点控制)

这样做的影响很大程度上是禁止某些优化。以代码为例

int i;

void foo(void) {
  i = 0;
  while(i == 0) {
    // do stuff that does not touch i
  }
}

编译器被允许使其成为一个从不检查的无限循环i再次因为它可以推断出i在循环中没有改变,因此i == 0永远不会是假的。即使有另一个线程或中断处理程序可能会发生变化,这也成立i。编译器不知道它们,也不关心它们。明确允许不关心。

将此与

int volatile i;

void foo(void) {
  i = 0;
  while(i == 0) { // Note: This is still broken, only a little less so.
    // do stuff that does not touch i
  }
}

现在编译器必须假设i可以随时改变,不能做这个优化。当然,这意味着如果您处理中断处理程序和线程,volatile对象是同步所必需的。然而,它们还不够。

What volatile isn't

What volatile不保证是原子访问。如果您习惯于嵌入式编程,这应该具有直观意义。如果愿意的话,请考虑以下 8 位 AVR MCU 的代码:

uint32_t volatile i;

ISR(TIMER0_OVF_vect) {
  ++i;
}

void some_function_in_the_main_loop(void) {
  for(;;) {
    do_something_with(i); // This is thoroughly broken.
  }
}

该代码被破坏的原因是访问i不是原子的——在 8 位 MCU 上不能是原子的。例如,在这个简单的情况下,可能会发生以下情况:

  • i is 0x0000ffff
  • do_something_with(i)即将被调用
  • 高两个字节i被复制到此调用的参数槽中
  • 此时定时器0溢出,主循环中断
  • ISR 变化i。的低两个字节i溢出,现在是0. i is now 0x00010000.
  • 主循环继续,低两个字节i被复制到参数槽中
  • do_something_with被称为0作为其参数。

类似的事情也可能发生在个人电脑和其他平台上。如果说有什么不同的话,那就是更复杂的架构可能会带来更多机会。

Takeaway

所以不,使用volatile还不错,你(经常)必须用裸机代码来完成它。然而,当你使用它时,你必须记住,它不是一根魔杖,你仍然需要确保自己不会被绊倒。在嵌入式代码中,通常有一种特定于平台的方法来处理原子性问题;例如,对于 AVR,通常的 crowbar 方法是在持续时间内禁用中断,如下所示

uint32_t x;
ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
  x = i;
}
do_something_with(x);

...其中ATOMIC_BLOCK宏调用cli()(禁用中断)之前和sei()(启用中断)如果事先启用的话。

C11 是第一个明确承认多线程存在的 C 标准,引入了一系列新的原子类型和内存防护操作,可用于线程间同步,并且在许多情况下利用volatile不必要。如果您可以使用它们,就使用它们,但它们可能需要一段时间才能到达所有常见的嵌入式工具链。有了它们,上面的循环就可以像这样修复:

atomic_int i;

void foo(void) {
  atomic_store(&i, 0);
  while(atomic_load(&i) == 0) {
    // do stuff that does not touch i
  }
}

...以其最基本的形式。更宽松的内存顺序语义的精确语义远远超出了 SO 答案的范围,因此我将在这里坚持使用默认的顺序一致的内容。

如果您对此感兴趣,Gil Hamilton 在评论中提供了一个链接,该链接指向使用 C11 原子的无锁堆栈实现的解释,尽管我不认为这是内存顺序语义本身的非常好的撰写。然而,C11 模型似乎与 C++11 内存模型密切相关,其中存在有用的演示here。如果我找到 C11 特定文章的链接,我稍后会将其放在这里。

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

挥发性及其有害影响 的相关文章

随机推荐

  • 内部类是轻量级的吗?

    是内部类比普通类更轻量级 还是说最终java编译内部类就像普通类一样 我知道java中的类本身并不都是非常轻量级的 并且它们占用了permgen内存的一部分 所以我想知道是否最好使用类似闭包的函数作为内部类 或者标准类是否也可以 内部类和匿
  • Xampp:网络浏览器不会显示本地主机页面

    我已经在 Windows7 64 位 上安装了 xampp 我想使用 PHP Apache 和 MySql Xampp控制面板上Apache服务器和MySql服务运行正常 但是当我在网络浏览器上运行本地主机时 它只显示连接 没有其他的 与以
  • 使用 Altair 制作仪表板

    我想使用优秀的 Altair 库来创建仪表板 有没有办法创建仪表板而不显示任何代码 我在这里看到一些非常好的例子 https altair viz github io case studies exploring weather html但
  • 如何在android中的两个或多个应用程序之间安全地共享数据?

    我正在为企业环境制作一个应用程序框架 其中涉及两个或多个应用程序之间共享设备内存中的数据 该数据需要存储在设备上并且仅少数应用程序可以访问 可以通过用于安装它们的证书来识别 另外 它需要存储在secure方式 以便其他第三方应用程序无法访问
  • 命名空间与类声明

    我是 C 新手 我似乎找不到任何相关信息 所以我会在这里询问 命名空间中的类是否必须声明 using System public class myprogram void main The console class does not ha
  • 如何在 Unix 中的文件每行末尾添加 100 个空格

    我有一个文件 每行应包含 200 个字符 我收到一个每行只有 100 个字符的源文件 我现在需要为每行添加 100 个额外的空格 如果空格很少 我们可以使用 sed 如下所示 sed s filename gt newfilename 既然
  • java.lang.NullPointerException(无错误消息)

    我知道我的这个问题已经被问过很多次了 我确实遵循了大部分答案 但这些答案都没有帮助我 所以这是我的问题 每当我同步我的项目时它总是失败 Gradle 控制台如下所示 执行任务 app generateDebugSources app gen
  • Python列表的滑动窗口[重复]

    这个问题在这里已经有答案了 有没有一种有效或优雅的方法来检索Python中列表的所有k大小子列表 例如 arr 2 3 5 7 11 13 我想要所有 3 元素子列表 result 2 3 5 3 5 7 5 7 11 7 11 13 我知
  • 每次循环访问 Handlebars.js 范围之外的变量

    我有一个handlebars js 模板 就像这样 externalValue
  • 为什么在 Gmail API 中搜索返回的结果与在 Gmail 网站中搜索的结果不同?

    我正在使用 gmail API 搜索用户的电子邮件 我创建了以下搜索查询 ticket after 2015 11 04 AND from me AND in trash 当我在 Gmail 的浏览器界面中运行此查询时 我收到 11 条消息
  • 无法让factory_girl在rails 3.0.5下运行,意外的tCONSTANT

    这是我的 Gemfile 配置 group development test do gem rspec rails gem factory girl gt 2 0 0 beta1 gem factory girl rails git gt
  • 在字符串末尾使用 JavaScript 的 parseInt

    我知道 parseInt myString 10 Never forget the radix 如果字符串中的第一个字符是数字 将返回一个数字 但是如果我有一个像 column5 这样的字符串并且想要将其增加到下一个 column6 我该如
  • C 中宏参数的类型检查

    是否可以对 define 宏的参数进行类型检查 例如 typedef enum REG16 A REG16 B REG16 C REG16 define read 16 reg16 read register 16u reg16 asser
  • 如何使用 Durandal 导航下拉菜单?

    我刚刚开始与 Durandal 合作 所有部分都已就位 并且正在使用热毛巾模板来加快速度 让我困惑的一件事是如何创建比按钮组更复杂的分层导航系统 这就是我想要的结果 ABCA1 B1 C1A2 B2 C2 A B 和 C 是顶级菜单 没有附
  • 提取字符串中的链接并返回对象数组

    我从服务器收到一个字符串 该字符串包含文本和链接 主要以 http https 和 www 开头 很少有不同 但如果不同也没关系 Example 简单文本 简单文本 简单文本 domain ext subdir 再次文本 文本 youban
  • 如何将 proto3 与 Hadoop/Spark 结合使用?

    我有几个依赖于的 proto 文件syntax proto3 我还有一个 Maven 项目 用于构建 Hadoop Spark 作业 Hadoop 2 7 1 和 Spark 1 5 2 我想在 Hadoop Spark 中生成数据 然后根
  • Visual Studio 2012 - 缺少所需文件“tracker.exe”

    我尝试编译源代码哭泣引擎3我总是收到以下错误消息 Error 1 error Required file tracker exe is missing C Program Files x86 MSBuild Microsoft Cpp v4
  • 如何在 Bokeh 0.13 中制作和弦图?

    以前版本的 Bokeh 似乎有和弦图 https docs bokeh org en 0 12 4 docs gallery chord chart html 但不是最新版本 https docs bokeh org en latest d
  • ACTION_SEND 用于发送短信

    我想打开本机应用程序来发送短信 但应该已经有电话号码 我发现 ACTION SEND 但当我调用我的函数时 它返回错误 04 26 11 59 15 991 ERROR AndroidRuntime 20198 android conten
  • 挥发性及其有害影响

    我是一名嵌入式开发人员 在使用 I O 端口时使用 volatile 关键字 但我的项目经理建议使用 volatile 关键字是有害的 并且有很多缺点 但我发现在大多数情况下 volatile 在嵌入式编程中很有用 据我所知 挥发性在内核代