О setjmp / longjmp - PullRequest
       35

О setjmp / longjmp

11 голосов
/ 01 ноября 2011

Я исследовал setjmp / longjmp и обнаружил, что setjmp сохраняет регистры, такие как указатель инструкций, указатель стека и т. Д. *

Однако я не могу понять, что данные в стеке самого потока нельзя изменить между вызовами setjmp и longjmp . В этом случае longjmp не будет работать должным образом.

Чтобы прояснить, например, когда longjmp восстанавливает указатель стека, скажем, данные в памяти, на которую указывает указатель стека, не совпадают с тем, что было когда setjmp назывался. Может ли это случиться? И если это произойдет, разве мы не в беде?

Кроме того, что подразумевается под оператором: " Процедуры longjmp () не могут быть вызваны после возврата подпрограммы, которая вызвала процедуры setjmp (). "

Ответы [ 4 ]

6 голосов
/ 01 ноября 2011

setjmp()/longjmp() не предназначены для сохранения стека, для этого setcontext()/getcontext().

Стандарт определяет, что значение энергонезависимых автоматических переменных, определенных в функции, которая вызывает setjmp(), означает, чтоизменяются между setjmp() и longjmp() вызовы не определены после longjmp().По этой же причине существуют некоторые ограничения на то, как вы звоните setjmp().

5 голосов
/ 01 ноября 2011

Указатель стека отмечает деление между «использованной» и «неиспользуемой» частью стека. Когда вы вызываете setjmp, все текущие кадры вызова находятся на стороне «используется», и любые вызовы, которые происходят после setjmp, но до возврата функции, вызвавшей setjmp, имеют свои кадры вызова на «неиспользованном» сторона сохраненного указателя стека. Обратите внимание, что вызов longjmp после того, как функция, вызвавшая setjmp, вернулась, вызывает неопределенное поведение, поэтому не нужно рассматривать случай.

Теперь, возможно, что локальные переменные в некоторых из существующих фреймов вызова модифицируются после setjmp, либо вызывающей функцией, либо с помощью указателей, и это одна из причин, по которой необходимо использовать volatile во многих случаях. ..

3 голосов
/ 01 ноября 2011

Функция setjmp / longjmp (здесь и далее slj) в C ужасна, и ее поведение может варьироваться в зависимости от реализации. Тем не менее, учитывая отсутствие исключений, slj иногда необходим в C (обратите внимание, что C ++ предоставляет исключения, которые почти во всех отношениях превосходят slj, и что slj плохо взаимодействует со многими функциями C ++).

При использовании slj следует иметь в виду следующее, предполагая, что подпрограмма Parent () вызывает подпрограмму Setter (), которая вызывает setjmp (), а затем вызывает Jumper, который, в свою очередь, вызывает longjmp ().

  1. Код может по закону выходить из области, в которой выполняется setjmp без выполнения longjmp; однако, как только выходит из области действия, ранее созданный jmp_buf должен рассматриваться как недействительный. Компилятор, вероятно, не будет ничего делать, чтобы пометить его как таковой, но любая попытка использовать его может привести к непредсказуемому поведению, вероятно, включая переход к произвольному адресу.
  2. Любые локальные переменные в Jumper () будут испаряться при вызове longjmp (), делая их значения неактуальными.
  3. Всякий раз, когда элемент управления возвращается в Parent, с помощью любых средств, локальные переменные Parent будут такими, какими они были, когда они вызывали Setter, если только у таких переменных не были взяты их адреса и они были изменены с помощью таких указателей; в любом случае, setjmp / longjmp никак не повлияет на их значения. Если у таких переменных не взяты их адреса, возможно, что setjmp () может кэшировать значения таких переменных, а longjmp () может их восстановить. В этом случае, однако, переменные не смогут изменить время между кэшированием и восстановлением, поэтому кэш / восстановление не окажет видимого эффекта.
  4. Переменные в Setter могут кэшироваться или не кэшироваться с помощью вызова setjmp (). После вызова longjmp () такие переменные могут иметь значение, которое они имели при выполнении setjmp (), или значения, которые они имели, когда он вызывал подпрограмму, которая в конечном итоге вызывала longjmp (), или любую их комбинацию. По крайней мере, на некоторых диалектах C такие переменные могут быть объявлены как «volatile», чтобы предотвратить их кэширование.

Хотя setjmp / longjmp () иногда может быть полезен, он также может быть очень опасным. В большинстве случаев нет ошибочного кода защиты, вызывающего неопределенное поведение, и во многих реальных сценариях неправильное использование может привести к возникновению плохих вещей (в отличие от некоторых видов неопределенного поведения, где фактический результат часто может совпадать с тем, что программист предназначен).

0 голосов
/ 01 ноября 2011

В приведенном ниже примере setjmp / longjump изменяет значение i, которое находится в main, через указатель.Я никогда не увеличивается в цикле for.Для дополнительной забавы смотрите запись albert.c, http://www.ioccc.org/years-spoiler.html победитель IOCCC 1992 года.(один из немногих случаев, когда я ROTFLed читал источник C ...)

#include <stdio.h>
#include <setjmp.h>

jmp_buf the_state;

void helper(int *p);
int main (void)
{
int i;

for (i =0; i < 10;    ) {
    switch (setjmp (the_state) ) {
    case 0:  helper (&i) ; break;
    case 1:    printf( "Even=\t"); break;
    case 2:    printf( "Odd=\t"); break;
    default: printf( "Oops=\t"); break;
        }
    printf( "I=%d\n", i);
    }

return 0;
}
void helper(int *p)
{
*p += 1;
longjmp(the_state, 1+ *p%2);
}
...