【问题标题】:Identifying possibility of interference识别干扰的可能性
【发布时间】:2016-07-15 10:39:31
【问题描述】:

在多任务环境中。如果任务具有表达式y = x + x;,是否有可能在两次读取 x 之间发生中断(任务切换)。

【问题讨论】:

  • 简短的回答是这不太可能,但可能
  • 如果x 是由另一个线程同时修改和/或由信号处理程序异步修改的对象,并且a 不是原子的,也不是无锁原子的或@987654324 类型@ 在信号处理程序的情况下,行为是 undefined.
  • 在某些架构上,甚至不能保证可以使用单个原子指令获取单个 x(例如,long long 在 y = 2 * x 也不会是线程安全的。
  • 以上三个 cmets 可以有用地详细说明为答案 - 答案不应作为 cmets 发布。 cmets 的合法用途是解释为什么该问题值得被否决——对我来说这似乎是一个合理的问题——尽管标题有点模糊。

标签: c real-time interrupt multitasking rtos


【解决方案1】:

在大多数情况下,编译器生成的代码只会读取 x 一次,因为优化是微不足道的。事实上,它甚至可以将操作转换为右移,相当于x << 1。但是,您不能保证,如果 x 被声明为 volatile 它必然会进行两次读取,然后在读取低位和高位单词之间不会中断(反之亦然),在这种情况下y 的分配也是可中断的。

另一个问题是,如果 x 不是原子数据类型(即无法在单个指令中读取),例如 16 位目标上的 32 位类型,那么即使它是单次读取,它可能是可中断的。

在任何一种情况下,如果x 本身(或第二种情况下的y)在上下文之间共享(在这种情况下也应该声明为volatile,那么两次读取将是),这通常是一个问题必要),或者如果由于某种原因,分配的时间在某种程度上很关键,需要完全确定(不太可能)。

如果x 是共享的,因此volatile 是原子类型,则此示例中的简单解决方案是编写表达式y = x << 1y = x * 2 以确保@987654333 @ 只读一次。 x 不是原子的 - 您可能需要锁定调度程序或禁用中断(即使用关键部分),或者更有选择性地使用互斥锁保护访问。对于无法将表达式简化为对共享变量的单个引用的更复杂的表达式,只需将变量分配给非共享的局部临时变量即可确保仅读取一次。原子性问题仍然存在。

【讨论】:

    【解决方案2】:

    这里是 x86 类架构的具体答案,与传统调度程序一起使用 (https://en.wikipedia.org/wiki/Completely_Fair_Scheduler)。看看这个 asm 代码,由 gcc 为 x86 生成,优化设置为 -OS0。

    https://godbolt.org/g/AfKTkF

    int main(void) {
      int y;
      int x = 5;
      y = x + x;
    }
    

    转向

    main:
            pushq   %rbp
            movq    %rsp, %rbp
            movl    $5, -4(%rbp)
            movl    -4(%rbp), %eax  #read x
            addl    %eax, %eax      #execute x + x
            movl    %eax, -8(%rbp)
            movl    $0, %eax
            popq    %rbp
            ret
    

    如您所见,x 只被读取一次,这意味着即使有一个任务切换,x 上也不会有第二次读取。


    编辑: EOL 在 cmets 中指出 x 确实可以读取两次,修改上面的代码会产生以下结果:

    volatile x = 5;
    

    强制编译器插入两个 movl 操作(而不是一个):

    movl    -8(%rbp), %edx
    movl    -8(%rbp), %eax
    

    不过,这并没有让我的第一个猜测出错。编译器将优化变量的读取访问(如果它是简单类型)并删除第二次读取。 volatile 关键字强制编译器访问寄存器中的最新值。第一个代码不会强制读取 - 因此在线程切换和修改 x 的情况下会给出另一个结果。

    【讨论】:

    • 这仅适用于一种特定硬件和软件组合的情况
    • @M.M 鉴于问题中没有说明硬件/软件组合这一事实,我假设一个流行且广泛使用的组合。鉴于上下文切换 (linfo.org/context_switch.html) 总是几乎相同的事实。我可以编辑问题以明确说明这通常无效。这会消除我的反对票吗?
    • 解决其他反对意见:您能详细说明一下吗?我可以看到我只介绍了一个特殊情况,它仍然回答了 OP 的问题。
    • C 标准保证x 在这个表达式中只会被读取一次。事实上,就抽象机而言,x 被读取了两次(你可以通过volatile-qualifying x 来测试这个)。如果同时修改x,则行为未定义
    • @EOF:确实如此。不过,我们不应该失去焦点。 OP 询问这两个读取之间是否可能发生中断。我的第一个建议是——鉴于我们拥有的代码——不会发生中断,因为只有一个 movl 指令。 x 的值会发生什么不在我们的范围内。声明 x volatile 将强制执行两条读取指令,因此这两条指令之间可能会发生中断。还是我完全坚持这个?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-05
    • 2017-07-13
    • 2021-10-28
    • 1970-01-01
    • 1970-01-01
    • 2013-09-05
    相关资源
    最近更新 更多