setcontext es una de una familia de funciones de biblioteca de C (las otras son getcontext , makecontext y swapcontext ) utilizadas 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 este último permite solo un único salto no local en la pila , permite la creación de múltiples hilos de control cooperativos , cada uno con su propia pila.
setcontext setcontext
Especificación
setcontextse especificó en POSIX .1-2001 y en la Especificación Única de Unix , versión 2, pero no todos los sistemas operativos tipo Unix las proporcionan. POSIX .1-2004 dejó obsoletas estas funciones, [1] y en POSIX .1-2008 se eliminaron, y se indicó que POSIX Threads era un posible reemplazo.
Definiciones
Las funciones y los tipos asociados se definen en el archivo de encabezadoucontext.h del 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_linkapunta al contexto que se reanudará cuando salga del contexto actual, si el contexto se creó con makecontext(un contexto secundario). uc_sigmaskse utiliza para almacenar el conjunto de señales bloqueadas en el contexto, y uc_stackes la pila utilizada por el contexto. uc_mcontextalmacena el estado de ejecución , incluidos todos los registros y los indicadores de la 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 contexto enucp.setcontextno retorna.
- Esta función transfiere el control al contexto en
int getcontext(ucontext_t *ucp)- Guarda el contexto actual en
ucp. Esta función retorna en dos casos posibles: después de la llamada inicial o cuando un hilo cambia al contexto enucpviasetcontextoswapcontext. Lagetcontextfunción no proporciona un valor de retorno para distinguir los casos (su valor de retorno se usa únicamente para señalar un error), por lo que el programador debe usar una variable de indicador 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
makecontextfunción establece un hilo de control alternativo enucp, que se ha inicializado previamente congetcontext. Elucp.uc_stackmiembro debe apuntar a una pila de tamaño adecuado; la constanteSIGSTKSZse utiliza habitualmente. Cuandoucpse salta a utilizandosetcontextoswapcontext, la ejecución comenzará en el punto de entrada a la función a la que apuntafunc, conargclos argumentos especificados. Cuandofunctermina, el control se devuelve aucp.uc_link.
- La
int swapcontext(ucontext_t *oucp, ucontext_t *ucp)- Transfiere el control a
ucpy guarda el estado de ejecución actual enoucp.
- Transfiere el control a
Ejemplo
El siguiente ejemplo demuestra un iterador que utiliza setcontext.
#include <stdio.h> #include <stdlib.h> #include <ucontext.h> #include <señal.h>
/* Los tres contextos:
* (1) main_context1 : El punto en main al que regresará el bucle.
* (2) main_context2 : El punto en main al que
fluirá el control desde el bucle * cambiando de contexto.
* (3) loop_context : El punto en el bucle al que
fluirá el control desde main * cambiando de contexto. */
ucontext_t main_context1 , main_context2 , loop_context ;
/* El valor de retorno del iterador. */
volátil int i_from_iterator ;
/* Esta es la función iteradora. Se ingresa en la primera llamada a
* swapcontext, y recorre en bucle desde 0 hasta 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 * señalado por main_context1. */
void loop ( ucontext_t * loop_context , ucontext_t * other_context , int * i_from_iterator ) { int i ; for ( i = 0 ; i < 10 ; ++ i ) { /* Escribe el contador del bucle en la ubicación de retorno del iterador. */ * i_from_iterator = i ; /* Guarda el contexto del bucle (este punto en el código) en ''loop_context'', * y cambia 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 para la función iteradora. */ char iterator_stack [ SIGSTKSZ ];
/* Bandera que indica que el iterador se ha completado. */
volátil int iterator_finished ;
getcontext ( & loop_context );
/* Inicializa el contexto del iterador. uc_link apunta a main_context1, el * punto al que regresar cuando el iterador termina. */ loop_context . uc_link = & main_context1 ; loop_context . uc_stack . ss_sp = iterator_stack ; loop_context . uc_stack . ss_size = sizeof ( iterator_stack );
/* Rellene loop_context para que haga que swapcontext inicie el bucle. La
conversión de tipo * (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 ); /* Borre el indicador de finalizado. */ iterator_finished = 0 ;
/* Guarda el contexto actual en main_context1. Cuando finaliza el bucle,
* el flujo de control volverá a este punto. */
getcontext ( & main_context1 ); if ( ! iterator_finished ) { /* Establece iterator_finished de modo que cuando se * regrese al getcontext anterior a través de uc_link, la condición if anterior sea falsa y * el iterador no se reinicie. */ iterator_finished = 1 ; while ( 1 ) { /* Guarda este punto en main_context2 y cambia al iterador. * La primera llamada comenzará el bucle. Las llamadas posteriores cambiarán al * swapcontext en el bucle. */ swapcontext ( & main_context2 , & loop_context ); printf ( "%d \n " , i_from_iterator ); } } return 0 ; }
NOTA: este ejemplo no es correcto, [1] pero puede funcionar como se pretende en algunos casos. La función makecontextrequiere que los parámetros adicionales sean de tipo int, pero el ejemplo pasa punteros. Por lo tanto, el ejemplo puede fallar en máquinas de 64 bits (específicamente arquitecturas LP64 , donde ). Este problema se puede solucionar descomponiendo 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 puntero tienen el mismo tamaño (por ejemplo, x86-32, donde ambos tipos son de 32 bits), es posible que pueda pasar punteros como argumentos a makecontext() después de argc. Sin embargo, no se garantiza que esto sea portable, no está definido según los estándares y no funcionará en arquitecturas donde los punteros sean más grandes que int. No obstante, 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 el contexto, puede resultar útil un contexto más pequeño:
#include <stdio.h> #include <ucontext.h> #include <unistd.h>
int main ( int argc , const char * argv []){ ucontext_t contexto ; obtenercontext ( & contexto ); puts ( "Hola mundo" ); dormir ( 1 ); establecercontext ( & contexto ); devolver 0 ; }
Esto crea un bucle infinito porque el contexto contiene el contador del programa.
Referencias
- ^ ab Especificaciones básicas de The Open Group, número 6, IEEE Std 1003.1, edición 2004 [1]
Enlaces externos
- Contextos del sistema V - Manual de la biblioteca C de GNU
- : obtener/establecer el contexto actual del usuario – Manual del programador de Linux – Funciones de la biblioteca
- – Manual de funciones de la biblioteca de FreeBSD