SetContext является одним из семейства C библиотеке функций (другие являющиеся 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 г .: [1]
С включением стандарта 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
сохраняет состояние выполнения , включая все регистры и флаги ЦП , указатель команд и указатель стека ; является непрозрачным типом . mcontext_t
Функции:
int setcontext(const ucontext_t *ucp)
- Эта функция передает управление контексту в
ucp
. Выполнение продолжается с точки, в которой был сохранен контекстucp
.setcontext
не возвращается.
- Эта функция передает управление контексту в
int getcontext(ucontext_t *ucp)
- Сохраняет текущий контекст в
ucp
. Эта функция возвращает в двух возможных случаях: после первоначального вызова или когда поток переключается на контекстucp
черезsetcontext
илиswapcontext
.getcontext
Функция не обеспечивает возвращаемое значение , чтобы отличить случаи (возвращаемое значение используется исключительно для сигнала ошибки), поэтому программист должен использовать явный переменный флаг, который не должен быть переменным регистр и должен быть признан неустойчивой , чтобы избежать постоянного распространения или другие оптимизации компилятора .
- Сохраняет текущий контекст в
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 ) { / * Записать счетчик цикла в место возврата итератора. * / * i_from_iterator = i ; / * Сохраняем контекст цикла (этот пункт в коде) в '' loop_context '', * и переключаемся на other_context. * / swapcontext ( loop_context , other_context ); } / * Функция попадает в вызывающий контекст с неявным * '' 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 = итератор_стак ; loop_context . uc_stack . ss_size = sizeof ( итератор_стек ); / * Заполняем 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 ); } } возврат 0 ; }
ПРИМЕЧАНИЕ: этот пример неверен [1], но в некоторых случаях может работать должным образом. Функция makecontext
требует, чтобы дополнительные параметры были типом int
, но в примере передаются указатели. Таким образом, пример может не работать на 64-битных машинах (в частности, на архитектуре LP64, где ). Эту проблему можно обойти, разбив и восстановив 64-битные значения, но это приводит к снижению производительности.sizeof(void*) > sizeof(int)
В архитектурах, где типы int и указатели имеют одинаковый размер (например, x86-32, где оба типа 32-битные), вы можете избежать передачи указателей в качестве аргументов для makecontext () после argc. Однако переносимость этого не гарантируется, оно не определено в соответствии со стандартами и не будет работать на архитектурах, где указатели больше, чем ints. Тем не менее, начиная с версии 2.8, glibc вносит некоторые изменения в
, чтобы разрешить это на некоторых 64-битных архитектурах (например, x86-64).
Для получения и установки контекста может пригодиться меньший контекст:
#include #include #include int main ( int argc , const char * argv []) { контекст ucontext_t ;getcontext ( & context ); put ( "Привет, мир" ); сон ( 1 ); setcontext ( & context ); возврат 0 ; }
Это создает бесконечный цикл, потому что контекст содержит счетчик программы.
Рекомендации
Внешние ссылки
- Система V контексты - The GNU C Library Руководство
- : получить / установить текущий пользовательский контекст - Руководство программиста Linux - Библиотечные функции
- setcontext - получить / установить текущий пользовательский контекст справочной страницы FreeBSD.