установитьконтекст
setcontext — одна из семейства C библиотеки функций (остальные — getcontext , makecontext и swapcontext ), используемых для управления контекстом . setcontext
Семейство позволяет реализовать на языке C расширенные управления потоками шаблоны , такие как итераторы , волокна и сопрограммы . Их можно рассматривать как расширенную версию setjmp/longjmp ; тогда как последний допускает только один нелокальный переход вверх по стеку , setcontext
позволяет создавать несколько совместных потоков управления , каждый со своим стеком.
Спецификация
[ редактировать ]setcontext
был указан в POSIX .1-2001 и Единой спецификации Unix версии 2, но не все Unix-подобные операционные системы предоставляют их. В POSIX .1-2004 эти функции устарели. [1] а в POSIX .1-2008 они были удалены, а потоки POSIX в качестве возможной замены были указаны .
Определения
[ редактировать ]Функции и связанные типы определены в 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, ...)
- The
makecontext
функция устанавливает альтернативный поток управления вucp
, который ранее был инициализирован с использованиемgetcontext
.ucp.uc_stack
член должен быть указан на стек соответствующего размера; константаSIGSTKSZ
обычно используется. Когдаucp
переходит к использованиюsetcontext
илиswapcontext
, выполнение начнется с точки входа в функцию, на которую указываетfunc
, сargc
аргументы, как указано. Когдаfunc
завершается, управление возвращаетсяucp.uc_link
.
- The
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
- Передает управление
ucp
и сохраняет текущее состояние выполнения вoucp
.
- Передает управление
Пример
[ редактировать ]В приведенном ниже примере показан итератор с использованием setcontext
.
#include <stdio.h>
#include <stdlib.h>
#include <ucontext.h>
#include <signal.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;
}
ПРИМЕЧАНИЕ: этот пример неверен, [1] но в некоторых случаях может работать по назначению. Функция makecontext
требует дополнительных параметров для типа int
, но пример передает указатели. Таким образом, пример может не работать на 64-битных машинах (в частности, на LP64 -архитектурах, где sizeof(void*) > sizeof(int)
). Эту проблему можно обойти, разбивая и реконструируя 64-битные значения, но это приводит к снижению производительности.
В архитектурах, где типы int и указатели имеют одинаковый размер (например, x86-32, где оба типа имеют 32 бита), вам, возможно, удастся обойтись передачей указателей в качестве аргументов функции makecontext() после argc. Однако переносимость этого метода не гарантируется, он не определен в соответствии со стандартами и не будет работать на архитектурах, где указатели больше целых чисел. Тем не менее, начиная с версии 2.8, glibc вносит некоторые изменения в
, чтобы разрешить это на некоторых 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;
}
Это создает бесконечный цикл, поскольку контекст содержит счетчик программы.
Ссылки
[ редактировать ]- ^ Jump up to: а б Базовые спецификации открытой группы, выпуск 6 Стандарт IEEE 1003.1, издание 2004 г. [1]
Внешние ссылки
[ редактировать ]- Контексты System V — по библиотеке GNU C Руководство
- Linux программиста Руководство – Библиотечные функции : получить/установить контекст текущего пользователя –
- FreeBSD функциям библиотеки Руководство по –