setcontext - setcontext

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 г .:

С включением стандарта 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 <stdio.h>
#include <stdlib.h>
#include <ucontext.h>

/* The three contexts:
 *    (1) main_context1 : The point in main to which loop will return.
 *    (2) main_context2 : The point in main to which control from loop will
 *                        flow by switching contexts.
 *    (3) loop_context  : The point in loop to which control from main will
 *                        flow by switching contexts. */
ucontext_t main_context1, main_context2, loop_context;

/* The iterator return value. */
volatile int i_from_iterator;

/* This is the iterator function. It is entered on the first call to
 * swapcontext, and loops from 0 to 9. Each value is saved in i_from_iterator,
 * and then swapcontext used to return to the main loop.  The main loop prints
 * the value and calls swapcontext to swap back into the function. When the end
 * of the loop is reached, the function exits, and execution switches to the
 * context pointed to by 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)
{
    /* The stack for the iterator function. */
    char iterator_stack[SIGSTKSZ];

    /* Flag indicating that the iterator has completed. */
    volatile int iterator_finished;

    getcontext(&loop_context);
    /* Initialise the iterator context. uc_link points to main_context1, the
     * point to return to when the iterator finishes. */
    loop_context.uc_link          = &main_context1;
    loop_context.uc_stack.ss_sp   = iterator_stack;
    loop_context.uc_stack.ss_size = sizeof(iterator_stack);

    /* Fill in loop_context so that it makes swapcontext start loop. The
     * (void (*)(void)) typecast is to avoid a compiler warning but it is
     * not relevant to the behaviour of the function. */
    makecontext(&loop_context, (void (*)(void)) loop,
        3, &loop_context, &main_context2, &i_from_iterator);
   
    /* Clear the finished flag. */      
    iterator_finished = 0;

    /* Save the current context into main_context1. When loop is finished,
     * control flow will return to this point. */
    getcontext(&main_context1);
  
    if (!iterator_finished) {
        /* Set iterator_finished so that when the previous getcontext is
         * returned to via uc_link, the above if condition is false and the
         * iterator is not restarted. */
        iterator_finished = 1;
       
        while (1) {
            /* Save this point into main_context2 and switch into the iterator.
             * The first call will begin loop.  Subsequent calls will switch to
             * the swapcontext in loop. */
            swapcontext(&main_context2, &loop_context);
            printf("%d\n", i_from_iterator);
        }
    }
    
    return 0;
}

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

В архитектурах, где типы int и указатели имеют одинаковый размер (например, x86-32, где оба типа 32-битные), вы можете обойтись без передачи указателей в качестве аргументов для makecontext () после argc. Однако переносимость этого не гарантируется, оно не определено в соответствии со стандартами и не будет работать на архитектурах, где указатели больше, чем ints. Тем не менее, начиная с версии 2.8, glibc вносит некоторые изменения в

makecontext(3) , чтобы разрешить это на некоторых 64-битных архитектурах (например, x86-64).

Для получения и установки контекста может пригодиться меньший контекст:

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

int main(int argc, const char *argv[]){
	ucontext_t context;
	
	getcontext(&context);
	puts("Hello world");
	sleep(1);
	setcontext(&context);
	return 0;
}

Это создает бесконечный цикл, потому что контекст содержит счетчик программы.

Рекомендации

  1. ^ a b Базовые спецификации Open Group, выпуск 6 IEEE Std 1003.1, издание 2004 г. [1]

внешние ссылки