Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/linux/28.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 为什么volatile适用于setjmp/longjmp_C_Linux_Gcc_X86_Setjmp - Fatal编程技术网

C 为什么volatile适用于setjmp/longjmp

C 为什么volatile适用于setjmp/longjmp,c,linux,gcc,x86,setjmp,C,Linux,Gcc,X86,Setjmp,在调用longjmp()之后,如果非易失性限定本地对象的值在调用setjmp()后可能已更改,则不应访问这些对象。在这种情况下,它们的值被认为是不确定的,访问它们是未定义的行为 现在我的问题是为什么volatile在这种情况下有效?这个易失性变量的变化不会仍然使longjmp失败吗?例如,在下面给出的示例中,jmp的正确工作时间有多长?当代码在longjmp之后返回setjmp时,local_var的值不是1而是2吗 void some_function() { volatile int l

在调用longjmp()之后,如果非易失性限定本地对象的值在调用setjmp()后可能已更改,则不应访问这些对象。在这种情况下,它们的值被认为是不确定的,访问它们是未定义的行为

现在我的问题是为什么volatile在这种情况下有效?这个易失性变量的变化不会仍然使longjmp失败吗?例如,在下面给出的示例中,jmp的正确工作时间有多长?当代码在longjmp之后返回setjmp时,local_var的值不是1而是2吗

void some_function()
{
  volatile int local_var = 1;

  setjmp( buf );
  local_var = 2;
  longjmp( buf, 1 );
}

关键在于这种情况下的优化:优化器自然会期望对setjmp()之类的函数的调用不会更改任何局部变量,并优化对该变量的读取访问。例如:

int foo;
foo = 5;
if ( setjmp(buf) != 2 ) {
   if ( foo != 5 ) { optimize_me(); longjmp(buf, 2); }
   foo = 6;
   longjmp( buf, 1 );
   return 1;
}
return 0;

优化器可以优化掉optimize_me行,因为foo已经写在第2行中,不需要在第4行中读取,并且可以假设为5。此外,第5行中的赋值可以被删除,因为如果longjmp是一个普通的C函数,那么foo将不会被再次读取。但是,setjmp()和longjmp()以优化器无法解释的方式干扰了代码流,破坏了此方案。此代码的正确结果将是终止;随着线路的不断优化,我们有了一个无止境的循环。

setjmp
longjmp
clobber寄存器。如果变量存储在寄存器中,则其值在
longjmp
之后丢失


相反,如果它被声明为volatile,那么每次写入时,它都会被存储回内存,每次从中读取时,它都会从内存中读取。这会影响性能,因为编译器必须进行更多的内存访问,而不是使用寄存器,但这会使变量在
longjmp
ing时的使用更加安全。

在没有“volatile”限定符的情况下出现问题的最常见原因是编译器通常会将局部变量放入寄存器中。这些寄存器几乎肯定会用于setjmp和longjmp之间的其他事情。确保将这些寄存器用于其他目的不会导致变量在longjmp之后持有错误值的最实际的方法是将这些寄存器的值缓存在jmp_buf中。这是可行的,但有一个副作用,即编译器无法更新jmp_buf的内容,以反映缓存寄存器后对变量所做的更改

如果这是唯一的问题,那么访问未声明为volatile的局部变量的结果将是不确定的,但不是未定义的行为。尽管如此,thiton提到内存变量也存在一个问题:即使在堆栈上碰巧分配了一个局部变量,编译器在确定不再需要该变量的值时,也可以随时用其他变量覆盖该变量。例如,当一个例程调用其他例程时,编译器可以确定某些变量永远不会是“活动”的,将这些变量放在堆栈框架的最浅位置,并在调用其他例程之前将其弹出。在这种情况下,即使在调用setjmp()时内存中存在变量,该内存也可能被重新用于其他用途,如保存返回地址。因此,在执行longjmp()之后,内存将被视为未初始化


将“volatile”限定符添加到变量的定义中会导致只为该变量的使用保留存储,只要该变量在范围内。无论setjmp和longjmp之间发生什么情况,只要控件没有离开声明变量的范围,就不允许将该位置用于任何其他目的。

是否有任何方法可以减少由于volatile而产生的性能开销,同时仍能准确执行?多大的波动性会影响优化?您可以添加一些额外的代码,使其在调用
setjmp
时波动。类似于:
intx;/*dostuff*/volatile int save_x=x;如果(setjmp(buf)){x=save_x;/*dostuff*/}
。这通过使用非易失性变量在
setjmp
前后最大限度地提高了性能,但通过在整个调用中使用易失性变量确保了安全性。酷,这确实是一个很好的技巧!但我可以看到编译器不会尝试对数组使用寄存器,所以对于数组,volatile不会造成这样的差异,对吗?这会对性能产生多大的影响。我们不能告诉编译器使用一个寄存器作为变量,但在某个时候将它刷新到表示该变量的内存区域,这样当我们调用longjmp时,变量值就在那里了。@metallicprade:这是一个公平的问题。人们可能需要至少两种截然不同的volatile语义级别,其中一种可能比另一种具有更高的性能代价。我的猜测是编译器将使用允许最少优化的定义;如果使用这些变量的效率很重要,我建议声明volatile和non-volatile变量,并在setjmp之后将volatile变量复制到non-volatile变量,并且仅在关心longjmp()的情况下写入volatile变量如果我们在调用每个函数之前将所有寄存器都放入堆栈中,问题还会存在吗?我的意思是,在这种情况下,即使本地变量是通过寄存器访问的,它们也会被推入堆栈,然后弹出。不是吗?其次,假设即使不使用push,setjmp也会将寄存器保存在一个缓冲区中,那么返回为什么我们没有正确的变量值呢?我的意思是它应该是调用setjmp时的值。是吗?@metallicprist:通常情况下,longjmp()将从一个函数中调用,而该函数又由执行以下操作的函数调用