setcontext pertenece a una familia de funciones de biblioteca de C (las otras son getcontext , makecontext y swapcontext ) que se utilizan para el control de contexto . La familia permite la implementación en C de patrones de flujo de control avanzados como iteradores , fibras y corrutinas . Pueden verse como una versión avanzada de setjmp / longjmp ; mientras que el último permite solo un salto no local en la pila , permite la creación de múltiples subprocesos cooperativos de control setcontext
setcontext
, cada uno con su propia pila.
Especificación
setcontext
se especificó en POSIX .1-2001 y la Especificación Única de Unix , versión 2, pero no todos los sistemas operativos similares a Unix los proporcionan. POSIX .1-2004 dejó obsoletas estas funciones, y en POSIX .1-2008 se eliminaron, con POSIX Threads indicados como posible reemplazo. Citando IEEE Std 1003.1, Edición 2004: [1]
Con la incorporación del estándar ISO / IEC 9899: 1999 en esta especificación, se encontró que el estándar ISO C (Subcláusula 6.11.6) especifica que el uso de declaradores de funciones con paréntesis vacíos es una característica obsoleta. Por lo tanto, usando el prototipo de función:
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...);
está haciendo uso de una característica obsoleta de la norma ISO C. Por lo tanto, una aplicación POSIX estrictamente conforme no puede utilizar este formulario. Por lo tanto, el uso de getcontext (), makecontext () y swapcontext () está marcado como obsoleto.
En el estándar ISO C no hay forma de especificar un prototipo de función no obsoleta que indique que se llamará a una función con un número arbitrario (incluido cero) de argumentos de tipos arbitrarios (incluidos números enteros, punteros a datos, punteros a funciones y tipos compuestos).
Definiciones
Las funciones y los tipos asociados se definen en el archivo de encabezado del ucontext.h
sistema . Esto incluye el tipo con el que operan las cuatro funciones:ucontext_t
typedef struct { ucontext_t * uc_link ; sigset_t uc_sigmask ; stack_t uc_stack ; mcontext_t uc_mcontext ; ... } ucontext_t ;
uc_link
apunta al contexto que se reanudará cuando el contexto actual salga, si el contexto se creó con makecontext
(un contexto secundario). uc_sigmask
se utiliza para almacenar el conjunto de señales bloqueadas en el contexto, y uc_stack
es la pila utilizada por el contexto. uc_mcontext
almacena el estado de ejecución , incluidos todos los registros y banderas de CPU , el puntero de instrucción y el puntero de pila ; es un tipo opaco . mcontext_t
Las funciones son:
int setcontext(const ucontext_t *ucp)
- Esta función transfiere el control al contexto en
ucp
. La ejecución continúa desde el punto en el que se almacenó el contextoucp
.setcontext
no vuelve.
- Esta función transfiere el control al contexto en
int getcontext(ucontext_t *ucp)
- Guarda el contexto actual en
ucp
. Esta función regresa en dos casos posibles: después de la llamada inicial, o cuando un hilo cambia al contexto enucp
viasetcontext
oswapcontext
. Lagetcontext
función no proporciona un valor de retorno para distinguir los casos (su valor de retorno se usa únicamente para señalar el error), por lo que el programador debe usar una variable de bandera explícita, que no debe ser una variable de registro y debe declararse volátil para evitar la propagación constante. u otras optimizaciones del compilador .
- Guarda el contexto actual en
void makecontext(ucontext_t *ucp, void (*func)(), int argc, ...)
- La
makecontext
función configura un subproceso de control alternativo enucp
, que se ha inicializado previamente usandogetcontext
. Elucp.uc_stack
miembro debe apuntar a una pila de tamaño apropiado; la constanteSIGSTKSZ
se usa comúnmente. Cuandoucp
se salta a usarsetcontext
oswapcontext
, la ejecución comenzará en el punto de entrada a la función apuntada porfunc
, con losargc
argumentos especificados. Cuandofunc
termina, se vuelve a controlarucp.uc_link
.
- La
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)
- Transfiere el control
ucp
y guarda el estado de ejecución actual enoucp
.
- Transfiere el control
Ejemplo
El siguiente ejemplo muestra un iterador usando setcontext
.
#include #include #include / * Los tres contextos: * (1) main_context1: El punto en main al que volverá el bucle. * (2) main_context2: El punto en main al que fluirá el control desde el bucle * al cambiar de contexto. * (3) loop_context: El punto en el bucle al que fluirá el control desde main * al cambiar de contexto. * / ucontext_t main_context1 , main_context2 , loop_context ;/ * El valor de retorno del iterador. * / volatile int i_from_iterator ;/ * Esta es la función del iterador. Se ingresa en la primera llamada a * swapcontext, y se repite de 0 a 9. Cada valor se guarda en i_from_iterator, * y luego se usa swapcontext para regresar al bucle principal. El bucle principal imprime * el valor y llama a swapcontext para volver a la función. Cuando se llega al final * del bucle, la función sale y la ejecución cambia al contexto * al que apunta main_context1. * / Void loop ( ucontext_t * loop_context , ucontext_t * other_context , int * i_from_iterator ) { int i ; for ( i = 0 ; i < 10 ; ++ i ) { / * Escriba el contador de bucle en la ubicación de retorno del iterador. * / * i_from_iterator = i ; / * Guarde el contexto del ciclo (este punto en el código) en '' loop_context '', * y cambie a other_context. * / Swapcontext ( loop_context , other_context ); } / * La función pasa al contexto de llamada con un implícito * '' setcontext (& loop_context-> uc_link); '' * / } int main ( void ) { / * La pila de la función iteradora. * / Char iterator_stack [ SIGSTKSZ ]; / * Bandera que indica que el iterador se ha completado. * / volatile int iterator_finished ; getcontext ( & loop_context ); / * Inicializa el contexto del iterador. uc_link apunta a main_context1, el * punto al que volver cuando finaliza el iterador. * / loop_context . uc_link = & main_context1 ; loop_context . uc_stack . ss_sp = iterator_stack ; loop_context . uc_stack . ss_size = sizeof ( iterator_stack ); / * Rellena loop_context para que haga que swapcontext inicie un ciclo. El encasillado * (void (*) (void)) es para evitar una advertencia del compilador, pero * no es relevante para el comportamiento de la función. * / makecontext ( & loop_context , ( void ( * ) ( void )) loop , 3 , & loop_context , & main_context2 , & i_from_iterator ); / * Limpiar la bandera terminada. * / iterator_finished = 0 ; / * Guarda el contexto actual en main_context1. Cuando termine el bucle, * el flujo de control volverá a este punto. * / getcontext ( & main_context1 ); if ( ! iterator_finished ) { / * Establece iterator_finished de modo que cuando el getcontext anterior * se devuelva a través de uc_link, la condición if anterior sea falsa y el * iterador no se reinicie. * / iterator_finished = 1 ; while ( 1 ) { / * Guarde este punto en main_context2 y cambie al iterador. * La primera llamada comenzará en bucle. Las siguientes llamadas cambiarán al * swapcontext en bucle. * / swapcontext ( & main_context2 , & loop_context ); printf ( "% d \ n " , i_from_iterator ); } } return 0 ; }
NOTA: este ejemplo no es correcto, [1] pero puede funcionar según lo previsto en algunos casos. La función makecontext
requiere parámetros adicionales para ser tipo int
, pero el ejemplo pasa punteros. Por lo tanto, el ejemplo puede fallar en máquinas de 64 bits (específicamente en las arquitecturas LP64 , donde ). Este problema se puede solucionar dividiendo y reconstruyendo valores de 64 bits, pero eso introduce una penalización en el rendimiento.sizeof(void*) > sizeof(int)
En arquitecturas donde los tipos int y pointer son del mismo tamaño (por ejemplo, x86-32, donde ambos tipos son 32 bits), es posible que pueda salirse con la suya pasando punteros como argumentos a makecontext () después de argc. Sin embargo, no se garantiza que hacer esto sea portátil, no está definido de acuerdo con los estándares y no funcionará en arquitecturas donde los punteros son más grandes que los ints. Sin embargo, a partir de la versión 2.8, glibc realiza algunos cambios en
, para permitir esto en algunas arquitecturas de 64 bits (por ejemplo, x86-64).
Para obtener y establecer contexto, un contexto más pequeño puede ser útil:
#include #include #include int main ( int argc , const char * argv []) { ucontext_t context ;getcontext ( & context ); put ( "Hola mundo" ); dormir ( 1 ); setcontext ( & contexto ); return 0 ; }
Esto crea un bucle infinito porque el contexto contiene el contador del programa.
Referencias
- ^ a b Las especificaciones básicas de Open Group Issue 6 IEEE Std 1003.1, 2004 Edition [1]
enlaces externos
- Contextos de System V - El manual de la biblioteca GNU C
- : obtener / establecer el contexto de usuario actual - Manual del programador de Linux - Funciones de biblioteca
- setcontext: obtiene / establece el contexto de usuario actual de la página de manual de FreeBSD.