MAKECONTEXT(3) | Руководство программиста Linux | MAKECONTEXT(3) |
makecontext, swapcontext - управляет пользовательским контекстом
#include <ucontext.h>
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
int swapcontext(ucontext_t *oucp, const ucontext_t *ucp);
В окружении, подобном SysV, имеется тип данных mcontext_t, определённый в файле <ucontext.h>, и четыре функции — getcontext(3), setcontext(3), makecontext() и swapcontext(), которые позволяют контексту пользовательского уровня переключаться между несколькими нитями внутри одного процесса.
Описание типа и первых двух функций смотрите в getcontext(3).
Функция makecontext() изменяет контекст, на который указывает ucp (полученный из вызова getcontext(3)). Перед вызовом makecontext(), вызывающий должен выделить новый стек для этого контекста и присвоить его адрес ucp->uc_stack, и определить последующий контекст и присвоить его адрес ucp->uc_link.
Позднее, когда этот контекст активируется (с помощью setcontext(3) или swapcontext()), вызывается функция func и ей передаётся набор целочисленных аргументов (int), указанных после argc; вызывающий должен указать количество этих аргументов в argc. После возврата из функции активируется последующий контекст. Если указатель последующего контекста равен NULL, то нить завершается.
Функция swapcontext() сохраняет текущий контекст в структуру, на которую указывает oucp, и после этого активирует контекст, на который указывает ucp.
При успешном выполнении swapcontext() не возвращает выполнение (но мы можем вернуться позднее при активации oucp и это будет выглядеть как если бы swapcontext() вернула 0). При ошибке swapcontext() возвращает -1 и изменяет errno соответствующим образом.
Функции makecontext() и swapcontext() появились в glibc начиная с версии 2.1.
Описание терминов данного раздела смотрите в attributes(7).
Интерфейс | Атрибут | Значение |
makecontext() | Безвредность в нитях | MT-Safe race:ucp |
swapcontext() | Безвредность в нитях | MT-Safe race:oucp race:ucp |
SUSv2, POSIX.1-2001. В POSIX.1-2008 удалены определения makecontext() и swapcontext() со ссылкой на проблемы с переносимостью и рекомендацией переписать приложение с использование нитей POSIX.
Назначение ucp->uc_stack подобно описанному в sigaltstack(2), а именно: данная структура содержит начало и размер области памяти, которая будет использоваться как стек, независимо от направления роста стека. То есть, в пользовательской программе нет необходимости учитывать это направление.
В архитектурах, где тип int и указатель имеют одинаковый размер (например, x86-32, оба типа имеют размер 32 бита), вы можете передавать указатели в аргументах makecontext() после argc. Однако, это не гарантирует переносимость, не определено в стандарте и не работает на архитектурах, где указатели больше int. Тем не менее, начиная с версии 2.8, в glibc внесены изменения в makecontext(), которые позволяют это и на некоторых 64-битных архитектурах (например, x86-64).
В программе, показанной далее, демонстрируется использование getcontext(3), makecontext() и swapcontext(). Вот результат запуска этой программы:
$ ./a.out main: swapcontext(&uctx_main, &uctx_func2) func2: запущена func2: swapcontext(&uctx_func2, &uctx_func1) func1: запущена func1: swapcontext(&uctx_func1, &uctx_func2) func2: возврат func1: возврат main: выход
#include <ucontext.h> #include <stdio.h> #include <stdlib.h> static ucontext_t uctx_main, uctx_func1, uctx_func2; #define handle_error(msg) \ do { perror(msg); exit(EXIT_FAILURE); } while (0) static void func1(void) { printf("func1: запущена\n"); printf("func1: swapcontext(&uctx_func1, &uctx_func2)\n"); if (swapcontext(&uctx_func1, &uctx_func2) == -1) handle_error("swapcontext"); printf("func1: возврат\n"); } static void func2(void) { printf("func2: запущена\n"); printf("func2: swapcontext(&uctx_func2, &uctx_func1)\n"); if (swapcontext(&uctx_func2, &uctx_func1) == -1) handle_error("swapcontext"); printf("func2: возврат\n"); } int main(int argc, char *argv[]) { char func1_stack[16384]; char func2_stack[16384]; if (getcontext(&uctx_func1) == -1) handle_error("getcontext"); uctx_func1.uc_stack.ss_sp = func1_stack; uctx_func1.uc_stack.ss_size = sizeof(func1_stack); uctx_func1.uc_link = &uctx_main; makecontext(&uctx_func1, func1, 0); if (getcontext(&uctx_func2) == -1) handle_error("getcontext"); uctx_func2.uc_stack.ss_sp = func2_stack; uctx_func2.uc_stack.ss_size = sizeof(func2_stack); /* последующий контекст — f1(), если argc > 1 */ uctx_func2.uc_link = (argc > 1) ? NULL : &uctx_func1; makecontext(&uctx_func2, func2, 0); printf("main: swapcontext(&uctx_main, &uctx_func2)\n"); if (swapcontext(&uctx_main, &uctx_func2) == -1) handle_error("swapcontext"); printf("main: выход\n"); exit(EXIT_SUCCESS); }
sigaction(2), sigaltstack(2), sigprocmask(2), getcontext(3), sigsetjmp(3)
2019-03-06 | GNU |