WikiSort.ru - Программирование

ПОИСК ПО САЙТУ | о проекте

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

Спецификация

setcontext определён в POSIX.1-2001 и во второй версии Single UNIX Specification, однако доступен не во всех UNIX-подобных операционных системах. Функции и связанные с ними типы определены в заголовочном файле ucontext.h. В их число входит тип ucontext_t, с которым взаимодействуют все четыре функции:

typedef struct ucontext {
	struct ucontext *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 является непрозрачным (opaque) указателем.

Также определены следующие функции:

  • int setcontext(const ucontext_t *ucp)
Эта функция переносит управление в контекст в ucp. Исполнение продолжается с точки, на которой контекст был сохранён в ucp. В случае успешного выполнения возврата из setcontext не производится.
  • int getcontext(ucontext_t *ucp)
Сохраняет текущий контекст в ucp. Возврат из этой функции происходит в двух случаях: после первичного вызова или при переключении потока на контекст в ucp с помощью setcontext или swapcontext. Функция getcontext не предоставляет возвращаемого значения для разделения этих случаев (оно служит лишь для сообщения об ошибке), поэтому разработчик должен явным образом использовать переменную-флаг, объявленную без модификатора register и с модификатором 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. Подобный код можно встретить достаточно редко; вместо использования setcontext для реализации кооперативной многозадачности часто используется различные библиотеки-обёртки, например, GNU Portable Threads.

#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

/* Функция-итератор. Вход в неё осуществляется при первом вызове
 * 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'' и переключение на другой контекст. */
        swapcontext(loop_context, other_context);
    }
} 
 
int main(void)
{
    /* Три контекста:
     *    (1) main_context1 : указывает на main для возврата из цикла.
     *    (2) main_context2 : указывает на место переключения контекста в main
     *    (3) loop_context  : указывает на место в цикле, в которое будет 
     *                        переходить управление из main. */
    ucontext_t main_context1, main_context2, loop_context;
    
    /* Стек для функции итератора. */
    char iterator_stack[SIGSTKSZ];

    /* Флаг, сообщающий о завершении итератора. */
    volatile int iterator_finished;
   
    /* Возвращаемое значение итератора. */
    volatile int i_from_iterator;
   
    /* Инициализация контекста итератора. 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);
    getcontext(&loop_context);
    
    /* Заполнение 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 для отключения перезапуска итератора. */
        iterator_finished = 1;
       
        while (1) {
            /* Сохранение этой точки в main_context2 и переключение на итератор.
             * Первый вызов зачинает цикл, последующие осуществляют переключение
             * через swapcontext в цикл. */
            swapcontext(&main_context2, &loop_context);
            printf("%d\n", i_from_iterator);
        }
    }
    
    return 0;
}

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

Примечания

Ссылки

Данная страница на сайте WikiSort.ru содержит текст со страницы сайта "Википедия".

Если Вы хотите её отредактировать, то можете сделать это на странице редактирования в Википедии.

Если сделанные Вами правки не будут кем-нибудь удалены, то через несколько дней они появятся на сайте WikiSort.ru .




Текст в блоке "Читать" взят с сайта "Википедия" и доступен по лицензии Creative Commons Attribution-ShareAlike; в отдельных случаях могут действовать дополнительные условия.

Другой контент может иметь иную лицензию. Перед использованием материалов сайта WikiSort.ru внимательно изучите правила лицензирования конкретных элементов наполнения сайта.

2019-2024
WikiSort.ru - проект по пересортировке и дополнению контента Википедии