【问题标题】:How come a compiler cannot detect if a global variable is changed by another thread?为什么编译器无法检测到全局变量是否被另一个线程更改?
【发布时间】:2014-10-25 15:21:37
【问题描述】:

我刚刚浏览了 volatile 关键字的概念。我刚刚浏览了this link,这个链接讲述了为什么在程序使用中断处理程序的情况下使用volatile 关键字。他们在一个例子中提到过:

int etx_rcvd = FALSE;
void main()
{
    ...
    while (!ext_rcvd)
    {
        // Wait
    }
    ...
}

interrupt void rx_isr(void)
{
    ...
    if (ETX == rx_char)
    {
        etx_rcvd = TRUE;
    }
    ...
} 

他们说因为编译器无法知道ext_rcvd 在中断处理程序中得到更新。因此编译器使用优化智能并假定此变量值始终为FALSE,并且它永远不会进入while{} 条件。所以为了防止这种情况我们使用volatile关键字,它会阻止编译器使用自己的智能。

我的问题是,编译时,编译器怎么不知道ext_rcvd 正在中断处理程序中更新?请帮我找到它的答案,我没有得到正确的答案。

【问题讨论】:

    标签: c optimization compiler-construction compilation interrupt


    【解决方案1】:

    编译器无法分析所有可以修改ext_rcvd内存位置的代码或运行进程

    在您的示例中,您提到 ext_rcvd 正在中断处理程序中进行更新。那是对的。中断处理程序是操作系统在 CPU 接收到中断时启动的一段代码。那段代码实际上是驱动程序代码。在驱动程序代码中,ext_rcvd可能有另一个名称,但指向相同的内存位置。

    因此,为了知道 ext_rcvd 是否在其他地方更新,编译器需要分析库和驱动程序的代码,并确定它们正在更新您在代码中命名为 ext_rcvd 的完全相同的内存位置. 这不能在执行时间之前完成。

    多线程也是如此。如果某个线程正在更新另一个线程使用的确切内存位置,编译器无法知道先验。例如,如果另一个线程生成syscall(),那么编译器需要查看处理syscall() 的代码。

    【讨论】:

      【解决方案2】:

      当 CPU 接收到一个中断时,它会停止它正在做的任何事情(除非它正在处理一个更重要的中断,在这种情况下,它只会在更重要的中断完成时才处理这个中断),将某些参数保存在堆栈中并调用中断处理程序。这意味着中断处理程序本身不允许某些事情,因为系统处于未知状态。这个问题的解决方法是让中断处理程序立即做需要做的事情,通常是从硬件中读取一些东西或者向硬件发送一些东西,然后安排在以后的时间处理新的信息(这称为“下半部分”)并返回。然后保证内核尽快调用下半部分——当它调用时,内核模块中允许的所有内容都将被允许。

      我认为当调用中断时,任何工作都会停止并且变量设置为 TRUE,不满足 while 条件。 但是当你使用 volatile 关键字时,它会让 C 再次检查变量值。

      当然,我不是 100% 确定这一点,我愿意接受改变我的答案。

      【讨论】:

      • 但我的问题是编译器如何无法知道该变量正在被中断处理程序更新。同样的事情发生在多线程的情况下,编译器无法知道第二个线程正在更新这个值。编译在 CPU 上执行之前很多。
      【解决方案3】:

      interrupt 不是 C 指定的关键字,因此所讨论的内容都不是 C 指定的行为。

      是的,编译器可以看到etx_rcvdinterrupt例程内被修改,因此假设etx_rcvd可以在中断函数之外随时更改并生成int etx_rcvd --> volatile int etx_rcvd.

      现在的问题是应该这样做吗?

      IMO:不,不需要。

      interrupt 函数可以修改全局变量,并且代码流使得非中断函数访问仅发生在受中断保护的块中。将volatileint etx_rcvd 隐含会阻碍优化编译器。所以现在代码需要一种表达non_volatile int etx_rcvd 的方式来防止OP 寻求volatile 假设。

      C all ready 提供了一种方法来声明变量 volatile(添加 volatile)和 non-volatile(不要添加 volatile)。如果interrupt 例程可以在没有声明变量的情况下生成变量volatile,则代码将需要一个新关键字来确保非易失性。

      【讨论】:

        猜你喜欢
        • 2020-09-16
        • 2019-09-14
        • 1970-01-01
        • 2018-09-30
        • 2021-04-09
        • 2023-03-25
        • 1970-01-01
        • 2012-05-18
        • 1970-01-01
        相关资源
        最近更新 更多