Почему volatile работает для setjmp / longjmp - PullRequest
20 голосов
/ 03 ноября 2011

После вызова longjmp () к локальным объектам, не относящимся к энергонезависимым, не следует обращаться, если их значения могли измениться после вызова setjmp ().Их значение в этом случае считается неопределенным, и доступ к ним является неопределенным поведением.

Теперь мой вопрос: почему в этой ситуации работает volatile?Не изменится ли эта переменная переменная и все равно не получится longjmp?Например, как longjmp будет работать правильно в приведенном ниже примере?Когда код возвращается к setjmp после longjmp, не будет ли значение local_var 2 вместо 1?

void some_function()
{
  volatile int local_var = 1;

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

Ответы [ 3 ]

20 голосов
/ 03 ноября 2011

setjmp и longjmp регистры клоббера.Если переменная хранится в регистре, ее значение теряется после longjmp.

И наоборот, если она объявлена ​​как volatile, то каждый раз, когда она записывается, она сохраняется в памяти,и каждый раз, когда его читают, каждый раз читают из памяти.Это ухудшает производительность, потому что компилятор должен делать больше обращений к памяти вместо использования регистра, но это делает использование переменной безопасным перед лицом longjmp ing.

10 голосов
/ 03 ноября 2011

Суть заключается в оптимизации в этом сценарии: оптимизатор, естественно, ожидает, что вызов функции, такой как 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 может быть удалена, потому что foo никогда не будет читаться снова, если longjmp был нормальным C-функцией.Однако setjmp () и longjmp () нарушают поток кода таким образом, который оптимизатор не может объяснить, нарушая эту схему.Правильный результат этого кода будет прекращением;с оптимизированной линией у нас есть бесконечный цикл.

8 голосов
/ 03 ноября 2011

Наиболее распространенная причина проблем при отсутствии квалификатора 'volatile' заключается в том, что компиляторы часто помещают локальные переменные в регистры.Эти регистры почти наверняка будут использоваться для других целей между setjmp и longjmp.Наиболее практичный способ убедиться, что использование этих регистров для других целей не приведет к тому, что переменные будут содержать неправильные значения после longjmp, - это кэшировать значения этих регистров в jmp_buf.Это работает, но имеет побочный эффект: у компилятора нет возможности обновить содержимое jmp_buf, чтобы отразить изменения, внесенные в переменные после кэширования регистров.

Если бы это была единственная проблема,результат доступа к локальным переменным, не объявленным как volatile, будет неопределенным, но не неопределенным поведением.Однако существует проблема даже с переменными памяти, на которые ссылается этот пункт: даже если локальная переменная будет размещена в стеке, компилятор будет свободен перезаписать эту переменную чем-то другим в любое время, когда он определит, что ее значение больше не существует.необходимо.Например, компилятор может определить, что некоторые переменные никогда не являются «живыми», когда подпрограмма вызывает другие подпрограммы, поместить эти переменные как можно меньше в свой кадр стека и вытолкнуть их перед вызовом других подпрограмм.В таком случае, даже если переменные существовали в памяти при вызове setjmp (), эта память могла быть повторно использована для чего-то другого, например, для хранения адреса возврата.Таким образом, после выполнения longjmp () память будет считаться неинициализированной.

Добавление квалификатора 'volatile' к определению переменной приводит к тому, что память резервируется исключительно для использованияэта переменная, пока она находится в области видимости.Независимо от того, что происходит между setjmp и longjmp, при условии, что элемент управления не покинул область, в которой была объявлена ​​переменная, ничто не может использовать это местоположение для каких-либо других целей.

...