如果全局变量被中断修改,volatile修饰符是否真的需要? [英] Is volatile modifier really needed if global variables are modified by an interrupt?

查看:496
本文介绍了如果全局变量被中断修改,volatile修饰符是否真的需要?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有很多关于volatile变量的说法和写法,以及它们的用法。在这些文章中,可以找到两个稍有不同的想法:

<1> - 当编译程序之外的变量发生变化时,应该使用变量。

2 - 变量在函数正常流程之外变化时应该使用变量。

第一条语句将易失性用途限制在内存映射寄存器等,线程化的东西,但第二个实际上将中断添加到作用域。



这篇文章( http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword )例如明确指出:对于在中断期间更改的全局变量应该使用volatile修饰符,并提供此示例:

  int etx_rcvd = FALSE; 

void main()
{
...
while(!ext_rcvd)
{
//等待
}
...
}

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

注意如何设置一个rx_isr()作为回调在这里很方便地省略。
因此我写了自己的例子:

pre $ #include< stdio.h>
#include< time.h>
#include< signal.h>

void f();

int n = 0;

void main()
{
signal(2,f);
time_t tLastCalled = 0;
printf(输入循环\\\
);
while(n == 0)
{
if(time(NULL) - tLastCalled> 1)
{
printf(Still here ... \\ \
);
tLastCalled = time(NULL);
}
}
printf(Done \\\
);
}

void f()
{
n = 1;



$ b

使用不同级别的优化在Linux上编译gcc,每次退出循环,当我按ctrl + c时,我看到了完成,这意味着真正的gcc编译器足够智能,不会在这里优化变量n。

也就是说,我的问题是:

如果编译器能够真正优化由中断服务程序修改的全局变量,那么:

1.为什么它有权优化一个全局变量从另一个文件中调用?

2.为什么互联网上的示例文章和其他许多人声明编译器不会注意中断回调函数?

3.怎么做如果编译器能够真正优化由中断服务程序修改的全局变量,那么: / p>



  1. 为什么它有权优化第一个全局变量当它可能被另一个文件调用时?


这里的关键是在正常,没有中断的单线程程序,全局变量不能随时修改。无论访问哪个文件,对变量的所有访问都以可预测的方式进行排序。



优化可能很微妙。它不像啊,好吧这个全球似乎没有被使用,让我们完全删除它这么简单。相反,对于像/ b

 这样的一些代码,while(global)
{
do_stuff(global);
}

优化器可能会创建如下行为:

  register tmp = global; 
loop:
do_stuff(tmp);
转到循环;

这完全改变了程序的含义。缺乏易变体现本身所造成的这种缺陷总是与大小写不同。他们很难找到。






  1. 为什么网上的例子文章和其他许多人声明编译器不会注意中断回调函数?


因为嵌入式编译器在这方面传统上很愚蠢。传统上,当编译器发现非标准中断关键字时,它只会执行两件事:


  • 从该函数生成特定的返回码,因为与普通函数调用相比,中断通常具有不同的调用约定。

  • 确保函数被连接,即使它从未被程序调用过。可能分配在单独的内存段中。这实际上是由链接器而不是编译器完成的。



现在可能会有更聪明的编译器。在处理回调函数/线程时,PC /桌面编译器面临着同样的问题,但它们通常足够聪明,可以认识到它们不应该假设全局变量与回调共享。

在优化方面,嵌入式编译器传统上远比PC /桌面编译器逊色。在标准合规性方面,它们通常质量较差而且更差。如果你只是少数支持特定目标的编译器供应商之一,或者是唯一的供应商,那么缺乏竞争意味着你不必担心质量问题。您可以出售废话并为其收费。

但即使是好的编译器也可能会遇到这种情况,特别是那些不了解中断情况的多平台编译器等等。我们特意在target x中工作。

所以你有好的多平台编译器过于通用无法处理这个bug的情况。与此同时,target x这个糟糕而狭隘的编译器编写得太差,无法处理它,尽管它应该知道中断在target x上的工作方式。








  1. 如何修改我的代码以完成此操作?


使这样的全局变量 volatile


There was a lot said and written about volatile variables, and their use. In those articles, two slightly different ideas can be found:

1 - Volatile should be used when variable is getting changed outside of the compiled program.
2 - Volatile should be used when variable is getting changed outside the normal flow of the function.

First statement limits volatile usage to memory-mapped registers etc. and multi-threaded stuff, but the second one actually adds interrupts into the scope.

This article (http://www.barrgroup.com/Embedded-Systems/How-To/C-Volatile-Keyword) for example explicitly states that volatile modifier should be used for globals changed during the interrupt, and provides this example:

int etx_rcvd = FALSE;

void main() 
{
    ... 
    while (!ext_rcvd) 
    {
        // Wait
    } 
    ...
}

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

Note how setting up a rx_isr() as a callback is conveniently omitted here. Hence I wrote my own example:

#include <stdio.h>
#include <time.h>
#include <signal.h>

void f();

int n = 0;

void main() 
{
    signal(2,f);
    time_t tLastCalled = 0;
    printf("Entering the loop\n");
    while (n == 0) 
    {
        if (time(NULL) - tLastCalled > 1)
        {
            printf("Still here...\n");
            tLastCalled = time(NULL);
        }
    }
    printf ("Done\n");
}

void f() 
{
    n = 1;
}

Compiled with gcc on linux with various levels of optimizations, every time loop exited and I saw "Done" when I pressed ctrl+c, which means that really gcc compiler is smart enough not to optimize variable n here.

That said, my question is:
If compiler can really optimize global variables modified by an interrupt service routine, then:
1. Why it has a right of optimizing a global variable in the first place when it can possibly be called from another file?
2. Why the example article and many others on the internet state that the compiler will not "notice" the interrupt callback function?
3. How do I modify my code to accomplish this?

解决方案

If compiler can really optimize global variables modified by an interrupt service routine, then:

  1. Why it has a right of optimizing a global variable in the first place when it can possibly be called from another file?

The key here is that in a "normal", single-threaded program with no interrupts, the global variable cannot be modified by at any time. All access to the variable are sequenced in a predictable manner, no matter which file that does the access.

And the optimizations may be subtle. It is not as simple as "ah ok this global doesn't seem to be used, lets remove it entirely". Rather, for some code like

while(global) 
{ 
  do_stuff(global); 
} 

the optimizer might create something behaving like:

register tmp = global;
loop:
  do_stuff(tmp);
goto loop;

Which changes the meaning of the program completely. How such bugs caused by the lack of volatile manifest themselves is always different from case-to-case. They are very hard to find.


  1. Why the example article and many others on the internet state that the compiler will not "notice" the interrupt callback function?

Because embedded compilers are traditionally stupid when it comes to this aspect. Traditionally, when a compiler spots your non-standard interrupt keyword, it will just do 2 things:

  • Generate the specific return code from that function, since interrupts usually have different calling conventions compared to regular function calls.
  • Ensure that the function gets linked even though it is never called from the program. Possibly allocated in a separate memory segment. This is actually done by the linker and not the compiler.

There might nowadays be smarter compilers. PC/desktop compilers face the very same issue when dealing with callback functions/threads, but they are usually smart enough to realize that they shouldn't assume things about global variables shared with a callback.

Embedded compilers are traditionally far dumber than PC/desktop compilers when it comes to optimizations. They are generally of lower quality and worse at standard compliance. If you are one of just a few compiler vendors supporting a specific target, or perhaps the only vendor, then the lack of competition means that you don't have to worry much about quality. You can sell crap and charge a lot for it.

But even good compilers can struggle with such scenarios, especially multi-platform ones that don't know anything about how interrupts etc work specifically in "target x".

So you have the case where the good, multi-platform compiler is too generic to handle this bug. While at the same time, the bad, narrow compiler for "target x" is too poorly written to handle it, even though it supposedly knows all about how interrupts work on "target x".


  1. How do I modify my code to accomplish this?

Make such globals volatile.

这篇关于如果全局变量被中断修改,volatile修饰符是否真的需要?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆