setjmp.h - setjmp.h

редактировать

setjmp.h - это заголовок , определенный в стандартной библиотеке C для обеспечения «нелокальных переходов»: поток управления, который отличается от обычной подпрограммы, вызывающей и возвращающей последовательность. Дополнительные функции setjmpи longjmpобеспечивают эту функциональность.

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

Содержание

  • 1 Функции-члены
  • 2 Типы элементов
  • 3 Предостережения и ограничения
  • 4 Пример использование
    • 4.1 Простой пример
    • 4.2 Обработка исключений
    • 4.3 Совместная многозадачность
  • 5 См. также
  • 6 Ссылки
  • 7 Внешние ссылки

Функции-члены

int setjmp (jmp_buf env)Устанавливает локальный буфер jmp_bufи инициализирует его для перехода. Эта подпрограмма сохраняет среду вызова программы в буфере среды, заданном аргументом env, для дальнейшего использования longjmp. Если возврат от прямого вызова, setjmpвозвращает 0. Если возврат от вызова к longjmp, setjmpвозвращает ненулевое значение.
void longjmp (jmp_buf env, int value)Восстанавливает контекст буфера среды env, который был сохранен при вызове процедуры setjmpв том же вызове программа. Вызов longjmpиз вложенного обработчика сигналов имеет значение undefined. Значение, указанное в value, передается из longjmpв setjmp. После завершения longjmpвыполнение программы продолжается, как если бы соответствующий вызов setjmpтолько что вернулся. Если значение, переданное в longjmp, равно 0, setjmpбудет вести себя так, как если бы он вернул 1; в противном случае он будет вести себя так, как если бы он возвратил value.

setjmpсохраняет текущую среду (состояние программы) в какой-то момент выполнения программы в структуре данных, зависящей от платформы (jmp_buf), который может быть использован на более позднем этапе выполнения программы с помощью longjmpдля восстановления состояния программы до состояния, сохраненного setjmpв jmp_buf. Этот процесс можно представить как «прыжок» назад к точке выполнения программы, где setjmpсохранил среду. (Кажущееся) , возвращаемое значение из setjmpуказывает, достигло ли управление этой точки нормально (ноль) или от вызова longjmp(ненулевое). Это приводит к общей идиоме : if (setjmp (x)) {/ * handle longjmp (x) * /}.

POSIX.1 не указывает, должен ли setjmpи longjmpсохраняют и восстанавливают текущий набор заблокированных сигналов ; если программа использует обработку сигналов, она должна использовать POSIX sigsetjmp/ siglongjmp.

Типы членов

jmp_bufТип массива, например struct __jmp_buf_tag [1], подходит для хранения информации, необходимой для восстановления среды вызова.

Обоснование C99 описывает jmp_bufкак тип массива для обратной совместимости ; существующий код ссылается на места хранения jmp_bufпо имени (без оператора адреса ), что возможно только для типов массивов.

Предостережения и ограничения

Когда "нелокальный переход" выполняется через setjmp/ longjmpв C ++, нормальный "раскрутка стека " не происходит. Следовательно, никаких необходимых действий по очистке также не произойдет. Это может включать закрытие файловых дескрипторов, очистку буферов или освобождение памяти, выделенной кучей.

. Если функция, в которой был вызван setjmp, возвращается, больше невозможно безопасно использовать longjmpс соответствующим объектом jmp_buf. Это связано с тем, что кадр стека становится недействительным при возврате функции. Вызов longjmpвосстанавливает указатель стека , который - поскольку возвращенная функция - указывает на несуществующий и потенциально перезаписанный или поврежденный кадр стека.

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

По сравнению с механизмами в языках программирования более высокого уровня, таких как Python, Java, C ++, C# и даже до В языках C, таких как Algol 60, метод использования setjmp/ longjmpдля реализации механизма исключения является громоздким. Эти языки предоставляют более мощные методы обработки исключений, в то время как такие языки, как Scheme, Smalltalk и Haskell, предоставляют еще более общие Continuation - обрабатывающие конструкции.

Пример использования

Простой пример

В приведенном ниже примере показана основная идея setjmp. Там main ()вызывает first (), который, в свою очередь, вызывает second (). Затем second ()переходит обратно в main (), пропуская вызов first ()функции printf ().

#include #include static jmp_buf buf; void second () {printf ("второй \ п"); // выводит longjmp (buf, 1); // переходит обратно к тому месту, где был вызван setjmp - теперь setjmp возвращает 1} void first () {second (); printf ("первый \ п"); // не печатает} int main () {if (! setjmp (buf)) first (); // при выполнении setjmp вернул 0 else // когда longjmp перескочил назад, setjmp вернет 1 printf ("main \ n"); // выводит return 0; }

При выполнении вышеуказанная программа выведет:

second main

Обратите внимание, что, хотя вызывается подпрограмма first (), «first» никогда не печатается.. «main» печатается как условный оператор , если (! Setjmp (buf))выполняется во второй раз.

Обработка исключений

В этом примере setjmpиспользуется для обработки исключений в скобках, например попробуйте на некоторых других языках. Вызов longjmpаналогичен оператору throw, позволяя исключению возвращать статус ошибки непосредственно в setjmp. Следующий код соответствует стандарту 1999 ISO C и Single UNIX Specification, вызывая setjmpв ограниченном диапазоне контекстов:

  • В качестве условия для if, переключательили оператор итерации
  • Как указано выше, в сочетании с одиночным !или сравнением с целочисленной константой
  • В качестве оператора (с возвращаемым значением unused)

Следование этим правилам может упростить реализацию для создания буфера среды, что может быть деликатной операцией. Более общее использование setjmpможет вызвать неопределенное поведение, такое как повреждение локальных переменных; соответствующие компиляторы и среды не обязаны защищать или даже предупреждать о таком использовании. Однако несколько более сложные идиомы, такие как switch ((exception_type = setjmp (env))) {}, распространены в литературе и на практике и остаются относительно переносимыми. Ниже представлена ​​простая соответствующая методология, в которой вместе с буфером состояний поддерживается дополнительная переменная. Эта переменная может быть преобразована в структуру, включающую сам буфер.

В более современном примере обычный блок «try» будет реализован как setjmp (с некоторым кодом подготовки для многоуровневых прыжков, как показано в first), «throw "as longjmp с необязательным параметром в качестве исключения и блок" catch "в качестве блока" else "в разделе" try ".

#include #include #include #include static void first (); static void second (); / * Используем статическую переменную с файловой областью для стека исключений, чтобы мы могли * получить к нему доступ в любом месте этой единицы трансляции. * / static jmp_buf exception_env; статический int exception_type; int main (void) {char * volatile mem_buffer = NULL; if (setjmp (exception_env)) {// если мы попали сюда, то произошло исключение printf ("первая ошибка, тип исключения:% d \ n", exception_type); } else {// Запускаем код, который может сигнализировать об ошибке через longjmp. put ("сначала звонит"); первый(); mem_buffer = malloc (300); // выделяем ресурс printf ("% s \ n", strcpy (mem_buffer, "первый успешно")); // не достигнут} бесплатно (mem_buffer); // NULL можно передать в free, никаких операций не выполняется return 0; } static void first () {jmp_buf my_env; put ("входящий первым"); // достигли memcpy (my_env, exception_env, sizeof my_env); switch (setjmp (exception_env)) {case 3: // если мы попали сюда, возникло исключение. put ("второй сбой, тип исключения: 3; переназначение на тип 1"); исключение_типа = 1; default: // пропадать через memcpy (exception_env, my_env, sizeof exception_env); // восстанавливаем стек исключений longjmp (exception_env, exception_type); // продолжить обработку исключения case 0: // нормальная, желаемая операция put ("вызывающая вторая"); // достигли second (); put ("вторая удалась"); // не достигнуто} memcpy (exception_env, my_env, sizeof exception_env); // восстановление стека исключений put ("уход первым"); // никогда не достигал} static void second () {put ("ввод второй"); // достигнуто exception_type = 3; longjmp (исключение_env, тип_исключения); // объявляем, что программа не удалась put ("уходит второй"); // не достигнуто}

Эта программа выводит:

вызов первого ввода первого вызова второго ввода второго второго не удалось, тип исключения: 3; сначала не удалось переназначить тип 1, тип исключения: 1

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

В реальном мире setjmp-longjmp (sjlj) использовался как стандартный способ обработки исключений в сторонних компиляторах Windows C ++, поскольку нативная Обработка структурированных исключений не только плохо документирована. в общем, но также запатентовано на 32-битной Windows.

Совместная многозадачность

C99 предусматривает, что longjmpгарантированно будет работать только тогда, когда адресатом является вызывающая функция, т.е. что область назначения гарантированно останется неизменной. Переход к функции, которая уже завершилась с помощью returnили longjmp, не определена. Однако большинство реализаций longjmpспециально не уничтожают локальные переменные при выполнении перехода. Поскольку контекст сохраняется до тех пор, пока его локальные переменные не будут удалены, его можно восстановить с помощью setjmp. Во многих средах (например, Really Simple Threads и TinyTimbers ) такие идиомы, как if (! Setjmp (child_env)) longjmp (caller_env);могут разрешать вызываемая функция для эффективной приостановки и возобновления на setjmp.

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

Учитывая, что setjmpдля дочерней функции обычно будет работать, если не саботируется, и setcontext, как часть POSIX, не требуется предоставляемый реализациями C, этот механизм может быть переносимым, если альтернатива setcontextне работает.

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

#include #include jmp_buf mainTask, childTask; void call_with_cushion (); void child (); int main () {если (! setjmp (mainTask)) {call_with_cushion (); // дочерний процесс никогда не возвращается, yield} // выполнение возобновляется после этого "}" после первого раза, когда этот дочерний элемент дает результат while (1) {printf ("Parent \ n"); если (! setjmp (mainTask)) longjmp (childTask, 1); // yield - обратите внимание, что это не определено в C99}} void call_with_cushion () {char space [1000]; // Зарезервируйте достаточно места для main для запуска space [999] = 1; // Не допускать отсутствия оптимизации массива child (); } void child () {while (1) {printf ("Начало дочернего цикла \ n"); если (! setjmp (childTask)) longjmp (mainTask, 1); // yield - аннулирует childTask в C99 printf ("Конец дочернего цикла \ n"); если (! setjmp (childTask)) longjmp (mainTask, 1); // yield - делает childTask недействительным в C99} / * Не возвращать. Вместо этого мы должны установить флаг, указывающий, что main () должен перестать уступать нам, а затем longjmp (mainTask, 1) * /}

См. Также

  • значок Портал компьютерного программирования

Ссылки

Внешние ссылки

Последняя правка сделана 2021-06-08 01:39:28
Содержание доступно по лицензии CC BY-SA 3.0 (если не указано иное).
Обратная связь: support@alphapedia.ru
Соглашение
О проекте