【问题标题】:Can variables adjacent to a bit-field get corrupted?与位域相邻的变量会损坏吗?
【发布时间】:2017-07-09 05:33:57
【问题描述】:

我面临的问题与 Linux 内核社区所描述的问题非常相似 - Betrayed by a Bit-Field

问题的本质是 GCC 发出 64 位读取访问,甚至访问 1 位位域。这会导致读取相邻字段的意外副作用,可以在程序的其他地方进行修改。当修改的位域值被写回时,相邻变量的旧值也被写回,从而丢失了其他线程对其所做的任何修改。

我的问题略有不同。我有一个像这样的类/结构-

class Group {

    uint8 adjVariable;
    volatile bool  flag1: 1;
    volatile bool  flag2: 1;
    // so on...
    volatile bool  flag10: 1;
};

访问这些变量的方式是-

Group::fun() {
    Group_Scoped_lock();
    // adjVariable was 12 here.
    if ( adjVariable > 0 ) {
        adjVariable = 0; // <------- EXPLICIT ZERO ASSIGNMENT
    }
    // some code that doesn't affect adjVariable 
    bool1 = false;
    bool2 = false;
    bool3 = false;
    assert( adjVariable == 0 ); // <---- This assert is tripping stating that adjVariable is 12!!
}

在我们怀疑 GCC 存在“错误”之前,我验证了 adjVariable 是否在其他地方没有 Group_lock() 的情况下被访问。尽我所能,我无法在代码中看到发生这种情况的任何地方。

现在,由于编译器为位域发出 64 位读取并且它们是易失性的,如果它发出对 adjVariable 的读取作为此读取的一部分并且 adjVariable 的显式零分配仍在缓存中,因此我们读取adjVariable 的旧值 12?这个新读取的值会覆盖显式设置的值吗?因此我们将assert 绊倒了?如果是这样,我该如何验证?

在文章中,他们正在讨论丢失在其他线程中完成的相邻变量的更新,但在我的问题中,我怀疑我们正在丢失在 same 线程中完成的 adjVariable 更新因为从内存中读取。这可能吗?

我们在同样旧的 Fedora 12 版虚拟机上使用仅符合 C++98 的古老 g++ 编译器。此外,我们只在运行 6 个月的代码库中遇到过这个问题

【问题讨论】:

  • 您不能因为缓存效应而错过对变量的更新,除非您正在处理未定义的行为或其他错误。
  • 这在单个线程中或通过正确的互斥访问是不可能的。除非你的编译器有错误,否则就是这样。
  • 也许生成的程序集在这里也有帮助..
  • 如果这种情况以可重现的方式发生,那么反汇编会显示出一些亮点。这可能是一个错误的编译(不太可能给定 6 个月的时间间隔)、上下文切换和内存覆盖已保存的寄存器状态,或者是宇宙射线破坏了 cpu 寄存器。
  • 为什么会出现if ( adjVariable &gt; 0 )?没有多大意义

标签: c++ linux multithreading gcc


【解决方案1】:

如果adjVariable 未被任何其他并发线程访问,那么它几乎可以保证在断言点为 0。

虽然所有bools 都是一个单独的内存位置,并且确实会在它们之间产生一些奇怪的行为,但adjVariable 是一个单独的内存位置,编译器必须确保它的所有加载和存储看起来以符合源代码的明确顺序发生。

如果编译器为位域发出 64 位 writes,那么它必须通过将位域对齐到 8 个字节来保护相邻的内存位置(例如,adjVariable 之间应该有 7 个字节的填充和flag1)。不过,我看不出 64 位 reads 在这里会如何影响正确性。

虽然 内存位置 的概念仅适用于 C++11 及更高版本,但逻辑仍然适用于 C++98:adjVariable 在断言中不为零的唯一方法应该是让另一个线程写信给adjVariable。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-01
    • 1970-01-01
    相关资源
    最近更新 更多