时间:2021-07-01 10:21:17 帮助过:15人阅读
仅从语法上讲,这似乎毫无意义,先取其地址,在通过指针取其值。而实际上不然,多了一个关键词 volatile,所以它的含义就是强制编译器每次使用 x 都从内存中获取。
原因:
可以通过几个例子看一下。
1. 循环中有每次都要读取的全局变量:
- <span style="color: #000000">…
- </span><span style="color: #0000ff">static</span> <span style="color: #0000ff">int</span><span style="color: #000000"> should_continue;
- </span><span style="color: #0000ff">static</span> <span style="color: #0000ff">void</span> do_something(<span style="color: #0000ff">void</span><span style="color: #000000">);
- …
- </span><span style="color: #0000ff">while</span><span style="color: #000000"> (should_continue)
- do_something();</span>
假设 do_something() 函数中并没有对变量 should_continue 做任何修改,那么,编译器完全有可能把它优化成:
- <span style="color: #000000">…
- </span><span style="color: #0000ff">if</span><span style="color: #000000"> (should_continue)
- </span><span style="color: #0000ff">for</span><span style="color: #000000"> (;;)
- do_something();</span>
这很好理解,不是吗?对于单线程的程序,这么做完全没问题,可是对于多线程,问题就出来了:如果这个线程在执行do_something() 的期间,另外一个线程改变了 should_continue 的值,那么上面的优化就是完全错误的了!更严重的问题是,编译器根本就没有办法知道这段代码是不是并发的,也就无从决定进行的优化是不是正确的!
这里有两种解决办法:1) 给 should_continue 加锁,毕竟多个进程访问和修改全局变量需要锁是很自然的;2) 禁止编译器做此优化。加锁的方法有些过了,毕竟 should_continue 只是一个布尔,而且退一步讲,就算每次读到的值不是最新的 should_continue 的值也可能是无所谓的,大不了多循环几次,所以禁止编译器做优化是一个更简单也更容易的解决办法。我们使用 ACCESS_ONCE() 来访问 should_continue:
- <span style="color: #000000">…
- </span><span style="color: #0000ff">while</span><span style="color: #000000"> (ACCESS_ONCE(should_continue))
- do_something();</span>
2. 指针读取一次,但要dereference多次:
- <span style="color: #000000">…
- p </span>=<span style="color: #000000"> global_ptr;
- </span><span style="color: #0000ff">if</span> (p && p->s && p->s-><span style="color: #000000">func)
- p</span>->s->func();
那么编译器也有可能把它编译成:
- <span style="color: #000000">…
- </span><span style="color: #0000ff">if</span> (global_ptr && global_ptr->s && global_ptr->s-><span style="color: #000000">func)
- global_ptr</span>->s->func();
你可以谴责编译器有些笨了,但事实上这是C标准允许的。这种情况下,另外的进程做了 global_ptr = NULL; 就会导致后一段代码 segfault,而前一段代码没问题。同上,所以这时候也要用 ACCESS_ONCE():
- <span style="color: #000000">…
- p </span>=<span style="color: #000000"> ACCESS_ONCE(global_ptr);
- </span><span style="color: #0000ff">if</span> (p && p->s && p->s-><span style="color: #000000">func)
- p</span>->s->func();
3. watchdog 中的变量:
- <span style="color: #0000ff">for</span><span style="color: #000000"> (;;) {
- still_working </span>= <span style="color: #800080">1</span><span style="color: #000000">;
- do_something();
- }</span>
假设 do_something() 定义是可见的,而且没有修改 still_working 的值,那么,编译器可能会把它优化成:
- still_working = <span style="color: #800080">1</span><span style="color: #000000">;
- </span><span style="color: #0000ff">for</span><span style="color: #000000"> (;;) {
- do_something();
- }</span>
如果其它进程同时执行了:
- <span style="color: #0000ff">for</span><span style="color: #000000"> (;;) {
- still_working </span>= <span style="color: #800080">0</span><span style="color: #000000">;
- sleep(</span><span style="color: #800080">10</span><span style="color: #000000">);
- </span><span style="color: #0000ff">if</span> (!<span style="color: #000000">still_working)
- panic();
- }</span>
通过 still_working 变量来检测 wathcdog 是否停止了,并且等待10秒后,它确实停止了,panic()!经过编译器优化后,就算它没有停止也会 panic!!所以也应该加上 ACCESS_ONCE():
- <span style="color: #0000ff">for</span><span style="color: #000000"> (;;) {
- ACCESS_ONCE(still_working) </span>= <span style="color: #800080">1</span><span style="color: #000000">;
- do_something();
- }</span>
ACCESS_ONCE的作用
标签:内核 ORC def 而且 void 程序 watchdog 含义 watch