Scroll to navigation

setjmp(3) Library Functions Manual setjmp(3)

ИМЯ

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

LIBRARY

Standard C library (libc, -lc)

СИНТАКСИС

#include <setjmp.h>
int setjmp(jmp_buf env);
int sigsetjmp(sigjmp_buf env, int savesigs);
[[noreturn]] void longjmp(jmp_buf env, int val);
[[noreturn]] 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()

Функции 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, C99.

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

ЗАМЕЧАНИЯ

POSIX does not specify whether setjmp() will save the signal mask (to be later restored during longjmp()). In System V it will not. In 4.3BSD it will, and there is a function _setjmp() that will not. The behavior under Linux depends on the glibc version and the setting of feature test macros. Before glibc 2.19, setjmp() follows the System V behavior by default, but the BSD behavior is provided if the _BSD_SOURCE feature test macro is explicitly defined and none of _POSIX_SOURCE, _POSIX_C_SOURCE, _XOPEN_SOURCE, _GNU_SOURCE, or _SVID_SOURCE is defined. Since glibc 2.19, <setjmp.h> exposes only the System V version of setjmp(). Programs that need the BSD semantics should replace calls to setjmp() with calls to sigsetjmp() with a nonzero savesigs argument.

Функции 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)

ПЕРЕВОД

Русский перевод этой страницы руководства был сделан Alexander Golubev <fatzer2@gmail.com>, Azamat Hackimov <azamat.hackimov@gmail.com>, Hotellook, Nikita <zxcvbnm3230@mail.ru>, Spiros Georgaras <sng@hellug.gr>, Vladislav <ivladislavefimov@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.

5 февраля 2023 г. Linux man-pages 6.03