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