SETJMP(3) Руководство программиста Linux SETJMP(3)

ИМЯ

setjmp, sigsetjmp, longjmp, siglongjmp - выполняет нелокальный переход

ОБЗОР

#include <setjmp.h>
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
void longjmp(jmp_buf env, int val);
void siglongjmp(sigjmp_buf env, int val);

Требования макроса тестирования свойств для glibc (см. feature_test_macros(7)):

setjmp(): смотрите ЗАМЕЧАНИЯ.

sigsetjmp(): _POSIX_C_SOURCE

ОПИСАНИЕ

Функции, описываемые в этой странице, используются для выполнения «нелокального goto»: передачи исполнения из одной функции в заранее определённое место другой. Функция setjmp() динамически устанавливает точку для будущей передачи исполнения, а longjmp() выполняет передачу исполнения.

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

Функция longjmp() использует информацию, сохранённую в env, для передачи управления обратно в точку, откуда была вызвана setjmp(), и восстанавливает («отматывает») стек до состояния на время вызова setjmp(). Также, в зависимости от реализации (смотрите ЗАМЕЧАНИЯ), значения некоторых регистров маска сигналов процесса могут быть восстановлены в их состояние на момент вызова setjmp().

После успешного вызова longjmp() выполнение продолжается как если бы setjmp() была вызвана второй раз. Этот «фиктивный» возврат можно распознать от настоящего вызова setjmp(), так как «фиктивный» возврат возвращает значение, указанное в val. Если программист ошибочно передаст значение 0 в val, то «фиктивный» возврат вернёт вместо него 1.

Функции sigsetjmp() и siglongjmp() также выполняют нелокальные переходы, но предоставляют предсказуемую обработку сигнальной маски процесса.

Если, и только если, аргумент savesigs, передаваемый в sigsetjmp(), не равен нулю, то текущая маска сигналов процесса сохраняется в env и будет восстановлена, если позднее будет запущена siglongjmp() с этим env.

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функции setjmp() и sigsetjmp() возвращают 0, если вызывают явно; при «фиктивном» возврате, который возникает после longjmp() или siglongjmp(), возвращается ненулевое значение, указанное в val.

Функции longjmp() и siglongjmp() не выполняют возврат.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes(7).

Интерфейс Атрибут Значение
setjmp(), sigsetjmp() Безвредность в нитях MT-Safe
longjmp(), siglongjmp() Безвредность в нитях MT-Safe

СООТВЕТСТВИЕ СТАНДАРТАМ

setjmp(), longjmp(): POSIX.1-2001, POSIX.1-2008, C89, C99.

sigsetjmp(), siglongjmp(): POSIX.1-2001, POSIX.1-2008.

ЗАМЕЧАНИЯ

В POSIX не уточняется, должна ли setjmp() сохранять сигнальную маску (чтобы позднее восстановить её при longjmp()). В System V — не должна. В 4.3BSD сохранение выполняется и есть функция _setjmp, в которой этого не происходит. Поведение в Linux зависит от версии glibc и установленных макросов тестирования свойств. В Linux с glibc до версии 2.19 функция setjmp() по умолчанию ведёт себя как в System V, но можно включить поведение как в BSD, если определить макрос тестирования свойств _BSD_SOURCE и будут не определены _POSIX_SOURCE, _POSIX_C_SOURCE, _XOPEN_SOURCE, _GNU_SOURCE или _SVID_SOURCE. Начиная с glibc 2.19, в <setjmp.h> представляется только версия System V для setjmp(). Если в программе нужно задействовать семантику BSD, замените вызовы setjmp() на вызовы sigsetjmp() с ненулевым аргументом savesigs.

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

Компилятор может оптимизировать переменные в регистрах и longjmp() может восстановить значения регистров помимо указателя стека и счётчика программы. Следовательно, значения автоматических переменных непредсказуемы после вызова longjmp(), если они удовлетворяют следующим критериям:

  • они локальны для функции, которая сделала соответствующий вызов setjmp();
  • их значения изменились между вызовами setjmp() и longjmp(); и
  • они не объявлены как volatile.

Аналогичные замечания относятся и к siglongjmp().

Нелокальные переходы и читаемость программы

Хотя этим и можно злоупотребить, обычный оператор C «goto» имеет преимущество в виде лексических отметок (оператор goto и метка перехода), которые позволяют программисту легко понять поток выполнения. Нелокальные переходы не имеют таких отметок: многократные вызовы setjmp() могут использовать одну переменную jmp_buf, то есть контекст переменной может изменяться на протяжении времени работы приложения. Следовательно, программисту придёт вчитываться в код для определения динамической точки перехода определённого вызова longjmp() (для облегчения жизни программиста в каждом вызове setjmp() должна использоваться уникальная переменная jmp_buf).

Дополнительная сложность: вызовы setjmp() и longjmp() даже могут быть в разных модулях исходного кода.

Таким образом, нелокальные переходы могут сделать программу тяжелее для понимания и поддержки, и, если это возможно, нужно использовать альтернативные варианты.

Предостережения

Если функция, вызвавшая setjmp() завершилась до вызова longjmp(), то поведение не определено. Результатом может стать маленький (или не очень) хаос.

Если в многонитевой программе вызов longjmp() использует буфер env, инициализированный вызовом setjmp() в другой нити, то поведение не определено.

В POSIX.1-2008 Technical Corrigendum 2 вызовы longjmp() и siglongjmp() добавлены в список функций async-signal-safe. Однако стандарт рекомендует избегать использования этих функций в обработчиках сигналов и указывает, что если эти функции вызваны из обработчика сигналов, который прервал вызов функции не async-signal-safe (или её эквивалент, например шагам exit(3), который возникают при возврате из начального вызова main()), то поведение не определено, если программа далее вызывает функцию не async-signal-safe. Единственным способом избежать неопределённого поведения, является проверка следующего:

  • После длинного перехода из обработчика сигналов программа не вызывает каких-либо функций не async-signal-safe и не возвращается из первоначального вызова в main().
  • Любой сигнал, чей обработчик выполняет длинный переход, должен быть заблокирован на время каждого вызова функции не async-signal-safe и не вызывать функции не async-signal-safe после возврата из начального вызова в main().

СМОТРИТЕ ТАКЖЕ

signal(7), signal-safety(7)

2017-03-13