longjmp - reservadas - stdio h en programacion
Multitarea usando setjmp, longjmp (4)
¿Hay una manera de implementar la multitarea usando las funciones setjmp
y longjmp
Como ya lo mencionó Sean Ogden, longjmp () no es bueno para realizar tareas múltiples, ya que solo puede mover la pila hacia arriba y no puede saltar entre pilas diferentes. No vayas con eso.
Como lo mencionó user414736, puede usar las funciones getcontext / makecontext / swapcontext, pero el problema es que no están completamente en el espacio del usuario. En realidad, llaman a la llamada sigprocmask () porque cambian la máscara de señal como parte del cambio de contexto. Esto hace que swapcontext () sea mucho más lento que longjmp (), y es probable que no desee las co-rutinas lentas.
A mi entender, no existe una solución estándar POSIX para este problema, así que compilé mi propia información de diferentes fuentes disponibles. Puede encontrar las funciones de manipulación de contexto extraídas de libtask aquí:
https://github.com/stsp/dosemu2/tree/devel/src/arch/linux/mcontext
Las funciones son: getmcontext (), setmcontext (), makemcontext () y swapmcontext (). Tienen funciones semánticas similares a las funciones estándar con nombres similares, pero también imitan a la semántica setjmp (), ya que getmcontext () devuelve 1 (en lugar de 0) cuando es saltado por setmcontext ().
Además de eso, puede usar un puerto de libpcl, la biblioteca coroutine:
https://github.com/stsp/dosemu2/tree/devel/src/base/misc/libpcl
Con esto, es posible implementar el subprocesamiento cooperativo rápido de espacio de usuario. Funciona en linux, en arcos i386 y x86_64.
Esta es una forma de lo que se conoce como cambio de contexto del espacio de usuario.
Es posible, pero propenso a errores, especialmente si utiliza la implementación predeterminada de setjmp y longjmp. Un problema con estas funciones es que en muchos sistemas operativos solo guardarán un subconjunto de registros de 64 bits, en lugar de todo el contexto. Esto a menudo no es suficiente, por ejemplo, cuando se trata de bibliotecas de sistemas (mi experiencia aquí es con una implementación personalizada para amd64 / windows, que funcionó bastante estable considerando todos los aspectos).
Dicho esto, si no está intentando trabajar con bases de código externas complejas o controladores de eventos, sabe lo que está haciendo y (especialmente) si escribe su propia versión en el ensamblador que guarda más del contexto actual (si si está usando Windows de 32 bits o Linux, esto podría no ser necesario; si usa algunas versiones de BSD, imagino que casi definitivamente lo es), y lo depura prestando mucha atención a la salida del desmontaje, entonces podrá lograr lo que usted quiere.
Puede que esté doblando un poco las reglas, pero GNU pth hace esto. Es posible, pero probablemente no debería intentarlo usted mismo, excepto como un ejercicio de prueba de concepto académico, use la implementación pth si quiere hacerlo seriamente y de manera remota y portátil: entenderá por qué cuando lee El código de creación de hilos pth.
(Esencialmente usa un manejador de señales para engañar al sistema operativo para que cree una pila nueva, luego longjmp''s y mantiene a la pila alrededor. Funciona, evidentemente, pero es muy impreciso).
En el código de producción, si su sistema operativo admite makecontext / swapcontext, use esos en su lugar. Si es compatible con CreateFiber / SwitchToFiber, use esos en su lugar. Y tenga en cuenta la decepcionante verdad de que uno de los usos más convincentes de las coroutinas, es decir, invertir el control al ceder los controladores de eventos a los que llama el código extranjero, no es seguro porque el módulo de llamadas debe ser reentrante, y generalmente puede No pruebes eso. Es por eso que las fibras aún no son compatibles con .NET ...
Usted puede de hecho. Hay un par de maneras de lograrlo. La parte difícil es inicialmente obtener los jmpbufs que apuntan a otras pilas. Longjmp solo se define para los argumentos jmpbuf que fueron creados por setjmp, por lo que no hay forma de hacerlo sin usar ensamblaje o explotar un comportamiento indefinido. Los subprocesos de nivel de usuario son inherentemente no portátiles, por lo que la portabilidad no es un argumento sólido para no hacerlo realmente.
paso 1 Necesitas un lugar para almacenar los contextos de diferentes subprocesos, así que haz una cola de estructuras jmpbuf para la cantidad de subprocesos que desees.
Paso 2 Necesitas unir una pila para cada uno de estos hilos.
Paso 3 Debe obtener algunos contextos jmpbuf que tienen punteros de pila en las ubicaciones de memoria que acaba de asignar. Puede inspeccionar la estructura jmpbuf en su máquina, averiguar dónde almacena el puntero de pila. Llame a setjmp y luego modifique su contenido para que el puntero de pila esté en una de sus pilas asignadas. Las pilas generalmente crecen hacia abajo, por lo que probablemente querrá el puntero de su pila en algún lugar cerca de la ubicación de memoria más alta. Si escribe un programa C básico y usa un depurador para desmontarlo, y luego encuentra las instrucciones que ejecuta cuando regresa de una función, puede averiguar cuál debería ser el desplazamiento. Por ejemplo, con las convenciones de llamadas del sistema V en x86, verá que aparece% ebp (el puntero del marco) y luego llama a ret, que saca la dirección de retorno de la pila. Así que al entrar en una función, empuja la dirección de retorno y el puntero del marco. Cada pulsación mueve el puntero de la pila hacia abajo en 4 bytes, por lo que desea que el puntero de la pila comience en la dirección alta de la región asignada, -8 bytes (como si acabara de llamar una función para llegar allí). A continuación rellenaremos los 8 bytes.
La otra cosa que puede hacer es escribir un conjunto en línea muy pequeño (una línea) para manipular el puntero de la pila y luego llamar a setjmp. En realidad, esto es más portátil, porque en muchos sistemas los punteros de un jmpbuf están modificados para garantizar la seguridad, por lo que no puede modificarlos fácilmente.
No lo he probado, pero es posible que pueda evitar el asm simplemente desbordando deliberadamente la pila al declarar una matriz muy grande y, por lo tanto, mover el puntero de la pila.
Paso 4 Necesita salir de los hilos para devolver el sistema a algún estado seguro. Si no haces esto, y uno de los subprocesos regresa, tomará la dirección justo encima de tu pila asignada como una dirección de retorno y saltará a alguna ubicación de basura y probablemente a un error de seguridad. Así que primero necesitas un lugar seguro para volver. Obtenga esto llamando a setjmp en el hilo principal y almacenando el jmpbuf en una ubicación accesible globalmente. Defina una función que no tome argumentos y solo llame a longjmp con el jmpbuf global guardado. Obtenga la dirección de esa función y cópiela en las pilas asignadas donde dejó espacio para la dirección de devolución. Puedes dejar el puntero del cuadro vacío. Ahora, cuando un hilo regresa, irá a esa función que llama longjmp, y saltará directamente al hilo principal en el que llamaste setjmp, cada vez.
Paso 5 Justo después del setjmp del hilo principal, desea tener un código que determine qué hilo saltar al siguiente, sacando el jmpbuf apropiado de la cola y llamando a longjmp para ir allí. Cuando no hay hilos dejados en esa cola, el programa está listo.
Paso 6 Escriba una función de cambio de contexto que llame a setjmp y almacene el estado actual en la cola, y luego longjmp en otro jmpbuf de la cola.
Conclusión Eso es lo básico. Mientras los subprocesos sigan llamando al cambio de contexto, la cola se volverá a rellenar y se ejecutarán diferentes subprocesos. Cuando un hilo vuelve, si queda algo por ejecutar, el hilo principal elige uno y, si no queda ninguno, el proceso finaliza. Con un código relativamente pequeño puede tener una configuración multitarea cooperativa bastante básica. Probablemente quiera hacer más cosas, como implementar una función de limpieza para liberar la pila de un hilo muerto, etc. También puede implementar la prevención usando señales, pero eso es mucho más difícil porque setjmp no guarda el registro de punto flotante Los registros de estado o de banderas, que son necesarios cuando el programa se interrumpe de forma asíncrona.