Setcontext - setcontext

setcontext один из семьи C библиотека функции (остальные getcontext, makecontext и swapcontext) используется для контекст контроль. В setcontext семейство позволяет реализовать на языке C расширенные поток управления узоры Такие как итераторы, волокна, и сопрограммы. Их можно рассматривать как расширенную версию setjmp / longjmp; тогда как последний позволяет только один нелокальный прыжок вверх куча, setcontext позволяет создавать несколько кооператив нити контроля, каждый со своим стеком.

Технические характеристики

setcontext было указано в POSIX.1-2001 и Единая спецификация Unix, версия 2, но не все Unix-подобный операционные системы предоставить их. POSIX.1-2004 устарели эти функции, а в POSIX.1-2008 они были удалены, с Потоки POSIX указана в качестве возможной замены. Ссылаясь на IEEE Std 1003.1, издание 2004 г.[1]:

С включением стандарта ISO / IEC 9899: 1999 в эту спецификацию было обнаружено, что стандарт ISO C (подраздел 6.11.6) определяет, что использование деклараторов функций с пустыми круглыми скобками является устаревшей функцией. Следовательно, используя прототип функции:

пустота makecontext(ucontext_t *UCP, пустота (*func)(), int argc, ...);

использует устаревшую функцию стандарта ISO C. Следовательно, приложение, строго соответствующее POSIX, не может использовать эту форму. Поэтому использование getcontext (), makecontext () и swapcontext () помечено как устаревшее.

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

Определения

Функции и связанные типы определены в ucontext.h система заголовочный файл. Это включает ucontext_t тип, с которым работают все четыре функции:

typedef структура {	ucontext_t *uc_link;	sigset_t    uc_sigmask;	stack_t     uc_stack;	mcontext_t  uc_mcontext;	...} ucontext_t;

uc_link указывает на контекст, который будет возобновлен при выходе из текущего контекста, если контекст был создан с помощью makecontext (вторичный контекст). uc_sigmask используется для хранения набора сигналы заблокирован в контексте, и uc_stack это куча используется контекстом. uc_mcontext магазины исполнение государственный, включая все регистры и ЦПУ флаги, то указатель инструкции, а указатель стека; mcontext_t является непрозрачный тип.

Функции:

  • int setcontext(const ucontext_t *UCP)
    Эта функция передает управление контексту в UCP. Выполнение продолжается с точки, в которой контекст был сохранен в UCP. setcontext не возвращается.
  • int getcontext(ucontext_t *UCP)
    Сохраняет текущий контекст в UCP. Эта функция возвращает в двух возможных случаях: после первоначального вызова или когда поток переключается на контекст в UCP через setcontext или же swapcontext. В getcontext функция не обеспечивает возвращаемое значение чтобы различать случаи (его возвращаемое значение используется исключительно для сообщения об ошибке), поэтому программист должен использовать явную флаговую переменную, которая не должна быть регистровой переменной и должна быть объявлена летучий избежать постоянное распространение или другой оптимизация компилятора.
  • пустота makecontext(ucontext_t *UCP, пустота (*func)(), int argc, ...)
    В makecontext функция устанавливает альтернативный поток управления в UCP, который ранее был инициализирован с использованием getcontext. В ucp.uc_stack элемент должен быть указан в стеке подходящего размера; постоянная SIGSTKSZ обычно используется. Когда UCP перескакивает к использованию setcontext или же swapcontext, исполнение начнется в входная точка к функции, на которую указывает func, с argc аргументы, как указано. Когда func прекращается, управление возвращается ucp.uc_link.
  • int swapcontext(ucontext_t *аупп, ucontext_t *UCP)
    Передает контроль UCP и сохраняет текущее состояние выполнения в аупп.

Пример

В приведенном ниже примере демонстрируется итератор, использующий setcontext.

#включают <stdio.h>#включают <stdlib.h>#включают <ucontext.h>/ * Три контекста: * (1) main_context1: точка в main, к которой цикл вернется. * (2) main_context2: Основная точка, в которую будет осуществляться управление из цикла. * поток путем переключения контекстов. * (3) loop_context: точка в цикле, к которой будет осуществляться управление из основного * поток путем переключения контекстов. * /ucontext_t main_context1, main_context2, loop_context;/ * Возвращаемое значение итератора. * /летучий int i_from_iterator;/ * Это функция итератора. Он вводится при первом вызове * swapcontext и циклы от 0 до 9. Каждое значение сохраняется в i_from_iterator, * а затем swapcontext используется для возврата в основной цикл. Основной цикл печатает * значение и вызывает swapcontext для обратного переключения в функцию. Когда конец * цикла достигается, функция завершается, и выполнение переключается на * контекст, на который указывает main_context1. * /пустота петля(    ucontext_t *loop_context,    ucontext_t *other_context,    int *i_from_iterator){    int я;        за (я=0; я < 10; ++я) {        / * Записываем счетчик цикла в место возврата итератора. * /        *i_from_iterator = я;                / * Сохраняем контекст цикла (этот момент в коде) в '' loop_context '',         * и переключитесь на other_context. * /        swapcontext(loop_context, other_context);    }        / * Функция попадает в вызывающий контекст с неявным     * '' setcontext (& loop_context-> uc_link); '' * /}  int главный(пустота){    / * Стек для функции итератора. * /    char iterator_stack[SIGSTKSZ];    / * Флаг, указывающий, что итератор завершил работу. * /    летучий int iterator_finished;    getcontext(&loop_context);    / * Инициализируем контекст итератора. uc_link указывает на main_context1,     * точка, к которой нужно вернуться, когда итератор закончит работу. * /    loop_context.uc_link          = &main_context1;    loop_context.uc_stack.ss_sp   = iterator_stack;    loop_context.uc_stack.ss_size = размер(iterator_stack);    / * Заполняем loop_context так, чтобы он создавал цикл запуска swapcontext. В     * (void (*) (void)) приведение типов позволяет избежать предупреждения компилятора, но это     * не имеет отношения к поведению функции. * /    makecontext(&loop_context, (пустота (*)(пустота)) петля,        3, &loop_context, &main_context2, &i_from_iterator);       / * Сбросить флаг завершения. * /          iterator_finished = 0;    / * Сохраняем текущий контекст в main_context1. Когда цикл закончен,     * поток управления вернется к этой точке. * /    getcontext(&main_context1);      если (!iterator_finished) {        / * Устанавливаем iterator_finished так, чтобы предыдущий getcontext был         * возвращается через uc_link, указанное выше, если условие ложно и         * итератор не перезапускается. * /        iterator_finished = 1;               пока (1) {            / * Сохраняем эту точку в main_context2 и переключаемся на итератор.             * Первый вызов запустит цикл. Последующие звонки переключатся на             * контекст подкачки в цикле. * /            swapcontext(&main_context2, &loop_context);            printf("% d п", i_from_iterator);        }    }        возвращаться 0;}

ПРИМЕЧАНИЕ: этот пример неверен[1], но в некоторых случаях может работать должным образом. Функция makecontext требует дополнительных параметров для типа int, но в примере передаются указатели. Таким образом, пример может не работать на 64-битных машинах (в частности, LP64 -архитектуры, где размер(пустота*) > размер(int)). Эту проблему можно обойти, разбив и восстановив 64-битные значения, но это приводит к снижению производительности.

В архитектурах, где типы int и указатели имеют одинаковый размер (например, x86-32, где оба типа - 32 бита), вы можете избежать передачи указателей в качестве аргументов функции tomakecontext () после argc. Однако переносимость этого не гарантируется, оно не определено в соответствии со стандартами и не будет работать на архитектурах, в которых указатели больше, чем целые числа. Тем не менее, начиная с версии 2.8, glibc вносит некоторые изменения в

makecontext (3), чтобы разрешить это на некоторых 64-битных архитектурах (например, x86-64).

Для получения и установки контекста может пригодиться меньший контекст:

#включают <stdio.h>#включают <ucontext.h>#включают <unistd.h>int главный(int argc, const char *argv[]){	ucontext_t контекст;		getcontext(&контекст);	ставит("Привет, мир");	спать(1);	setcontext(&контекст);	возвращаться 0;}

Это создает бесконечный цикл, потому что контекст содержит счетчик программы.

Рекомендации

  1. ^ а б Базовые спецификации Open Group, выпуск 6 IEEE Std 1003.1, издание 2004 г. [1]

внешняя ссылка