setcontext является одним из семейства функций C library (остальные являются getcontext, makecontext и swapcontext ), используемые для управления контекстом. Семейство setcontext
позволяет реализовать на C расширенные шаблоны потока управления , такие как итераторы, волокна и сопрограммы. Их можно рассматривать как расширенную версию setjmp / longjmp ; тогда как последний допускает только один нелокальный переход вверх по стеку , setcontext
позволяет создавать несколько кооперативных потоков управления, у каждого свой стек.
setcontext
была указана в POSIX.1-2001 и Single Unix Specification, версия 2, но не все Unix-подобные операционные системы предоставляют их. POSIX.1-2004 устарел эти функции, а в POSIX.1-2008 они были удалены, с указанием потоков POSIX в качестве возможной замены. Ссылаясь на IEEE Std 1003.1, издание 2004 г.:
С включением стандарта ISO / IEC 9899: 1999 в эту спецификацию было обнаружено, что стандарт ISO C (подпункт 6.11.6) определяет использование деклараторов функций с пустыми круглыми скобками является устаревшей функцией. Следовательно, при использовании прототипа функции:
void makecontext (ucontext_t * ucp, void (* func) (), int argc,...);
использует устаревшую функцию стандарта ISO C. Следовательно, приложение, строго соответствующее POSIX, не может использовать эту форму. Поэтому использование getcontext (), makecontext () и swapcontext () помечено как устаревшее.
В стандарте ISO C нет способа указать не устаревший прототип функции, указывающий, что функция будет вызываться с произвольным числом (включая ноль) аргументов произвольных типов (включая целые числа, указатели на данные, указатели на функции и составные типы).
Функции и связанные типы определены в ucontext.h
системном заголовочном файле. Сюда входит тип ucontext_t
, с которым работают все четыре функции:
typedef struct {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
хранит выполнение состояния, включая все регистры и CPU флаги, указатель инструкции и указатель стека ; mcontext_t
- это непрозрачный тип.
Функции:
int setcontext (const ucontext_t * ucp)
ucp
. Выполнение продолжается с точки, в которой контекст был сохранен в ucp
. setcontext
не возвращает.int getcontext (ucontext_t * ucp)
ucp
. Эта функция возвращается в двух возможных случаях: после первоначального вызова или когда поток переключается на контекст в ucp
через setcontext
или swapcontext
. Функция getcontext
не предоставляет возвращаемое значение для различения случаев (ее возвращаемое значение используется исключительно для сообщения об ошибке), поэтому программист должен использовать явную переменную флага, которая не должна быть регистровой переменной и должна быть объявлена volatile, чтобы избежать распространения константы или других оптимизаций компилятора .void makecontext (ucontext_t * ucp, void (* func) (), int argc,...)
makecontext
устанавливает альтернативный поток управления в ucp
, который ранее был инициализирован с помощью getcontext
. Элемент ucp.uc_stack
должен указывать на стек соответствующего размера; обычно используется константа SIGSTKSZ
. Когда ucp
переходит к использованию setcontext
или swapcontext
, выполнение начнется с точки входа для функции, на которую указывает func
с указанными аргументами argc
. Когда func
завершается, управление возвращается в ucp.uc_link
.int swapcontext (ucontext_t * oucp, ucontext_t * ucp)
ucp
и сохраняет текущее состояние выполнения в oucp
.Пример ниже демонстрирует итератор, использующий setcontext
.
#include#include #include / * три контекста: * (1) main_context1: точка в main, к которой цикл вернется. * (2) main_context2: Точка в main, к которой будет * переходить управление из цикла путем переключения контекстов. * (3) loop_context: Точка в цикле, к которой будет * переходить управление из основного путем переключения контекстов. * / ucontext_t main_context1, main_context2, loop_context; / * Возвращаемое значение итератора. * / volatile int i_from_iterator; / * Это функция итератора. Он вводится при первом вызове * swapcontext и циклически изменяется от 0 до 9. Каждое значение сохраняется в i_from_iterator, * и затем swapcontext используется для возврата в основной цикл. Основной цикл печатает * значение и вызывает swapcontext, чтобы переключиться обратно в функцию. Когда достигается конец * цикла, функция завершается, и выполнение переключается на контекст, на который * указывает main_context1. * / void loop (ucontext_t * loop_context, ucontext_t * other_context, int * i_from_iterator) {int i; for (i = 0; i < 10; ++i) { /* Write the loop counter into the iterator return location. */ *i_from_iterator = i; /* Save the loop context (this point in the code) into ''loop_context'', * and switch to other_context. */ swapcontext(loop_context, other_context); } /* The function falls through to the calling context with an implicit * ''setcontext(loop_context->uc_link); '' * /} int main (void) {/ * Стек для функции итератора. * / char iterator_stack [SIGSTKSZ]; / * Флаг, указывающий, что итератор завершил работу. * / volatile 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 = sizeof (iterator_stack); / * Заполняем loop_context так, чтобы он создавал цикл запуска swapcontext. Приведение типов * (void (*) (void)) призвано избежать предупреждения компилятора, но * не имеет отношения к поведению функции. * / makecontext (loop_context, (void (*) (void)) loop, 3, loop_context, main_context2, i_from_iterator); / * Сбросить флаг завершения. * / iterator_finished = 0; / * Сохраняем текущий контекст в main_context1. Когда цикл завершится, * поток управления вернется к этой точке. * / getcontext (main_context1); if (! iterator_finished) {/ * Установите iterator_finished так, чтобы, когда предыдущий getcontext * возвращался через uc_link, вышеуказанное условие if было ложным, а итератор * не перезапускался. * / iterator_finished = 1; while (1) {/ * Сохраните эту точку в main_context2 и переключитесь на итератор. * Первый вызов запустит цикл. Последующие вызовы переключатся на * контекст подкачки в цикле. * / swapcontext (main_context2, loop_context); printf ("% d \ n", i_from_iterator); }} return 0; }
ПРИМЕЧАНИЕ: этот пример неверен, но в некоторых случаях может работать должным образом. Функция makecontext
требует, чтобы дополнительные параметры имели тип int
, но в примере передаются указатели. Таким образом, пример может не работать на 64-битных машинах (в частности, LP64 -архитектурах, где sizeof (void *)>sizeof (int)
). Эту проблему можно обойти путем разделения и восстановления 64-битных значений, но это приводит к снижению производительности.
В архитектурах, где типы int и указатели имеют одинаковый размер (например, x86-32, где оба типа 32-битные), вы можете избежать передачи указателей в качестве аргументов makecontext () после argc. Однако переносимость этого не гарантируется, оно не определено в соответствии со стандартами и не будет работать на архитектурах, в которых указатели больше, чем целые числа. Тем не менее, начиная с версии 2.8, glibc вносит некоторые изменения в
makecontext(3)
, чтобы разрешить это на некоторых 64-битных архитектурах (например, x86-64).
Для получения и установки контекст, меньший контекст может быть удобен:
#include#include #include int main (int argc, const char * argv) {ucontext_t context; getcontext (context); put ("Привет, мир"); сон (1); setcontext (context); возврат 0; }
Это создает бесконечный цикл, потому что контекст содержит счетчик программы.
setcontext (3)
: получение / установка текущего пользовательского контекста - Linux Руководство программиста Руководство - Библиотечные функции