Jump to content

setjmp.h

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

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

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

[ редактировать ]
int
setjmp(jmp_buf env)
Настраивает локальный jmp_buf буфер и инициализирует его для перехода. Эта рутина [1] сохраняет среду вызова программы в буфере среды, указанном параметром env аргумент для дальнейшего использования longjmp. Если возврат происходит из прямого вызова, setjmp возвращает 0. Если возврат происходит от вызова longjmp, setjmp возвращает ненулевое значение.
void
longjmp(jmp_buf env, int value)
Восстанавливает контекст буфера среды env который был сохранен путем вызова setjmp рутина [1] в том же вызове программы. Вызов longjmp из вложенного обработчика сигнала не определено . Значение, указанное value передается от longjmp к setjmp. После longjmp завершено, выполнение программы продолжается, как если бы соответствующий вызов setjmp только что вернулся. Если value перешел к 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 Тип массива, подходящий для хранения информации, необходимой для восстановления вызывающей среды.

Обоснование C99 описывает jmp_buf как тип массива для обратной совместимости ; существующий код относится к jmp_buf места хранения по названию (без & оператор адреса), что возможно только для типов массивов. [2] Он отмечает, что это может быть просто массив длиной в один элемент, где единственный элемент является фактическими данными; действительно, это подход, используемый библиотекой GNU C , которая определяет тип как struct __jmp_bug_tag[1].

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

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

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

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

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

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

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

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

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

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

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

static jmp_buf buf;

void second() {
    printf("second\n");         // prints
    longjmp(buf, 1);            // jumps back to where setjmp was called - making setjmp now return 1
}

void first() {
    second();
    printf("first\n");          // does not print
}

int main() {
    if (!setjmp(buf))
        first();                // when executed, setjmp returned 0
    else                        // when longjmp jumps back, setjmp returns 1
        printf("main\n");       // prints

    return 0;
}

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

second
main

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

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

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

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

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

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

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

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

static void first();
static void second();

/* Use a file scoped static variable for the exception stack so we can access
 * it anywhere within this translation unit. */
static jmp_buf exception_env;
static int exception_type;

int main(void) {
    char* volatile mem_buffer = NULL;

    if (setjmp(exception_env)) {
        // if we get here there was an exception
        printf("first failed, exception type: %d\n", exception_type);
    } else {
        // Run code that may signal failure via longjmp.
        puts("calling first");
        first();

        mem_buffer = malloc(300); // allocate a resource
        printf("%s\n", strcpy(mem_buffer, "first succeeded")); // not reached
    }

    free(mem_buffer); // NULL can be passed to free, no operation is performed

    return 0;
}

static void first() {
    jmp_buf my_env;

    puts("entering first"); // reached

    memcpy(my_env, exception_env, sizeof my_env);

    switch (setjmp(exception_env)) {
        case 3: // if we get here there was an exception.
            puts("second failed, exception type: 3; remapping to type 1");
            exception_type = 1;

        default: // fall through
            memcpy(exception_env, my_env, sizeof exception_env); // restore exception stack
            longjmp(exception_env, exception_type); // continue handling the exception

        case 0: // normal, desired operation
            puts("calling second"); // reached 
            second();
            puts("second succeeded"); // not reached
    }

    memcpy(exception_env, my_env, sizeof exception_env); // restore exception stack

    puts("leaving first"); // never reached
}

static void second() {
    puts("entering second" ); // reached

    exception_type = 3;
    longjmp(exception_env, exception_type); // declare that the program has failed

    puts("leaving second"); // not reached
}

Вывод этой программы:

calling first
entering first
calling second
entering second
second failed, exception type: 3; remapping to type 1
first failed, exception type: 1

Кооперативная многозадачность

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

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

Это используется библиотеками потоков для обеспечения совместной многозадачности без использования setcontext или другие оптоволоконные средства.

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

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

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

jmp_buf mainTask, childTask;

void call_with_cushion();
void child();

int main() {
    if (!setjmp(mainTask)) {
        call_with_cushion(); // child never returns, yield
    } // execution resumes after this "}" after first time that child yields

    while (1) {
        printf("Parent\n");
        
        if (!setjmp(mainTask))
            longjmp(childTask, 1); // yield - note that this is undefined under C99
    }
}

void call_with_cushion() {
    char space[1000]; // Reserve enough space for main to run
    space[999] = 1; // Do not optimize array out of existence
    child();
}

void child() {
    while (1) {
        printf("Child loop begin\n");
        
        if (!setjmp(childTask))
            longjmp(mainTask, 1); // yield - invalidates childTask in C99

        printf("Child loop end\n");

        if (!setjmp(childTask))
            longjmp(mainTask, 1); // yield - invalidates childTask in C99
    }

    /* Don't return. Instead we should set a flag to indicate that main()
       should stop yielding to us and then longjmp(mainTask, 1) */
}
  1. ^ Перейти обратно: а б ISO C утверждает, что setjmp должен быть реализован как макрос, но POSIX явно заявляет, что не определено, будет ли setjmp это макрос или функция.
  2. ^ Перейти обратно: а б C99 Обоснование, версия 5.10, апрель 2003 г. , раздел 7.13
  3. ^ Конспекты лекций CS360 — Setjmp и Longjmp
  4. ^ setjmp(3). Архивировано 26 июля 2009 г. на Wayback Machine.
  5. ^ Перейти обратно: а б ISO/IEC 9899:1999 , 2005, 7.13.2.1:2 и сноска 211.
  6. ^ setjmp: установить точку перехода для нелокального перехода — Справочник по системным интерфейсам, Единая спецификация UNIX , версия 4 от The Open Group

Дальнейшее чтение

[ редактировать ]
  • Нидито, Франческо (2 июля 2016 г.). «Исключения в C с Longjmp и Setjmp» . Группы.Di.Unipi.it . Проверено 02 января 2024 г.
[ редактировать ]
Arc.Ask3.Ru: конец переведенного документа.
Arc.Ask3.Ru
Номер скриншота №: f9ac3ad0f42bd0a9cf9fb33273904277__1720314780
URL1:https://arc.ask3.ru/arc/aa/f9/77/f9ac3ad0f42bd0a9cf9fb33273904277.html
Заголовок, (Title) документа по адресу, URL1:
setjmp.h - Wikipedia
Данный printscreen веб страницы (снимок веб страницы, скриншот веб страницы), визуально-программная копия документа расположенного по адресу URL1 и сохраненная в файл, имеет: квалифицированную, усовершенствованную (подтверждены: метки времени, валидность сертификата), открепленную ЭЦП (приложена к данному файлу), что может быть использовано для подтверждения содержания и факта существования документа в этот момент времени. Права на данный скриншот принадлежат администрации Ask3.ru, использование в качестве доказательства только с письменного разрешения правообладателя скриншота. Администрация Ask3.ru не несет ответственности за информацию размещенную на данном скриншоте. Права на прочие зарегистрированные элементы любого права, изображенные на снимках принадлежат их владельцам. Качество перевода предоставляется как есть. Любые претензии, иски не могут быть предъявлены. Если вы не согласны с любым пунктом перечисленным выше, вы не можете использовать данный сайт и информация размещенную на нем (сайте/странице), немедленно покиньте данный сайт. В случае нарушения любого пункта перечисленного выше, штраф 55! (Пятьдесят пять факториал, Денежную единицу (имеющую самостоятельную стоимость) можете выбрать самостоятельно, выплаичвается товарами в течение 7 дней с момента нарушения.)