c - fuente - hilos en java pdf
¿La horquilla(se supone que debe estar) a salvo de los manejadores de señales en un programa con hilos? (5)
Un tenedor está listado como seguro de señal asíncrona, por lo que puede ser usado.
POSIX
La siguiente tabla de las Especificaciones básicas de Open Group [Open Group 2004], define un conjunto de funciones que son asíncronas: señal segura. Las aplicaciones pueden invocar estas funciones, sin restricción, desde el manejador de señales.
Asíncrono: funciones de seguridad de la señal.
tenedor()
Realmente no estoy seguro de los requisitos que POSIX impone a la seguridad de la fork
en presencia de hilos y señales. fork
está listado como una de las funciones async-signal-safe, pero si existe la posibilidad de que el código de la biblioteca haya registrado controladores pthread_atfork
que no sean async-signal-safe, ¿esto niega la seguridad de fork
? ¿Depende la respuesta de si el subproceso en el que se está ejecutando el manejador de señales podría estar en medio de usar un recurso que necesitan los manejadores de atfork? O dicho de otra manera, si los manejadores de atfork hacen uso de los recursos de sincronización (mutexes, etc.) pero se llama a la fork
desde un manejador de señales que se ejecuta en un subproceso que nunca accede a estos recursos, ¿el programa es compatible?
Sobre la base de esta pregunta, si la bifurcación "segura para subprocesos" se implementa internamente en la biblioteca del sistema utilizando los modismos sugeridos por pthread_atfork
(obtenga todos los bloqueos en el controlador de prefork y libere todos los bloqueos tanto en el controlador primario como secundario del forfork). ¿Alguna vez es seguro usarlo desde los manejadores de señales en un programa de subprocesos? ¿No es posible que el hilo que maneja la señal podría estar en medio de una llamada a malloc
o fopen
/ fclose
y mantener un bloqueo global, lo que resulta en un interbloqueo durante la fork
?
Finalmente, incluso si la fork
es segura en los controladores de señales, ¿es seguro fork
en un controlador de señales y luego regresar desde el controlador de señales, o si una llamada a un _exit
señales en un controlador de señales siempre necesita una llamada posterior a _exit
o uno de los exec
¿Familia de funciones antes de que regrese el manejador de señales?
Estoy agregando esta respuesta porque parece que fork()
probablemente ya no se considere asíncrono. Al menos este parece ser el caso con glibc
pero quizás el soporte ya no existe en POSIX. La respuesta actualmente marcada como "aceptada" parece llegar a la conclusión de que es segura, pero al menos en glibc
probablemente no sea el caso.
Sobre la base de esta pregunta, si la bifurcación "segura para subprocesos" se implementa internamente en la biblioteca del sistema utilizando los modismos sugeridos por pthread_atfork (obtenga todos los bloqueos en el controlador de prefork y libere todos los bloqueos tanto en el controlador primario como secundario del forfork). ¿Alguna vez es seguro usarlo desde los manejadores de señales en un programa de subprocesos? ¿No es posible que el hilo que maneja la señal podría estar en medio de una llamada a malloc o fopen / fclose y mantener un bloqueo global, lo que resulta en un interbloqueo durante la bifurcación?
¡En efecto! Parece que The Open Group resolvió eliminarlo de la lista por esta misma razón .
La solicitud de interpretación nº 37 de IEEE 1003.1c-1995 se refiere a pthread_atfork
.
El Comité de Interpretación cree que ... se deben hacer las siguientes adiciones explicativas: Pág. 78 línea 864 "Además, las invocaciones de los manipuladores de horquillas establecidas por pthread_atfork desde un tenedor llamado desde un manejador de señales deben ser seguras asíncronas".
glibc
error 4737 de glibc
identifica una resolución para que fork()
se desaloje de la lista de funciones async-safe y se posix_spawn()
para llenar su lugar. Desafortunadamente, se resolvió como WONTFIX
por lo que ni siquiera se actualizaron las páginas de manual.
Haciendo mi mejor esfuerzo para responder a todas las subpreguntas; Pido disculpas porque algo de esto es más vago de lo que idealmente debería ser:
Si existe la posibilidad de que el código de la biblioteca haya registrado manejadores pthread_atfork que no sean seguros para señales asíncronas, ¿esto niega la seguridad de la bifurcación?
Sí. La documentación de la bifurcación menciona explícitamente esto:
When the application calls fork() from a signal handler and any of the
fork handlers registered by pthread_atfork() calls a function that is
not asynch-signal-safe, the behavior is undefined.
Por supuesto, esto significa que no puede usar pthread_atfork()
con el propósito de hacer que las bibliotecas de múltiples subprocesos sean transparentes para los procesos que creen que son de un solo subproceso, porque ninguna de las funciones de sincronización de pthread es segura para las señales asíncronas; esto se observa como un defecto en la especificación, consulte http://www.opengroup.org/austin/aardvark/latest/xshbug3.txt (busque "L16723").
¿Depende la respuesta de si el subproceso en el que se está ejecutando el manejador de señales podría estar en medio de usar un recurso que necesitan los manejadores de atfork? O dicho de otra manera, si los manejadores de atfork hacen uso de los recursos de sincronización (mutexes, etc.) pero se llama a la bifurcación desde un manejador de señales que se ejecuta en un subproceso que nunca accede a estos recursos, ¿el programa es compatible?
Hablando estrictamente, la respuesta es no, porque según las especificaciones, las funciones son seguras para señales asíncronas o no lo son; No hay concepto de "seguro bajo ciertas circunstancias". En la práctica, es posible que se salga con la suya, pero sería vulnerable a una implementación torpe pero correcta que no particionó sus recursos de la manera que esperaba.
Sobre la base de esta pregunta, si la bifurcación "segura para subprocesos" se implementa internamente en la biblioteca del sistema utilizando los modismos sugeridos por pthread_atfork (obtenga todos los bloqueos en el controlador de prefork y libere todos los bloqueos tanto en el controlador primario como secundario del forfork). ¿Alguna vez es seguro usarlo desde los manejadores de señales en un programa de subprocesos? ¿No es posible que el hilo que maneja la señal podría estar en medio de una llamada a malloc o fopen / fclose y mantener un bloqueo global, lo que resulta en un interbloqueo durante la bifurcación?
Si se implementara de esa manera, entonces tiene razón, el fork()
de un controlador de señales nunca estaría seguro, porque intentar obtener un bloqueo podría interrumpirse si el hilo que lo llama ya lo tenía. Pero esto implica que una implementación que utiliza un método de este tipo no sería conforme.
Mirando a glibc como un ejemplo, no hace eso, sino que toma dos enfoques: en primer lugar, los bloqueos que obtiene son recursivos (por lo tanto, si el hilo actual ya los tiene, su número de bloqueos simplemente aumentará); Además, en el proceso hijo, simplemente sobrescribe unilateralmente todos los bloqueos. Vea este extracto de nptl/sysdeps/unix/sysv/linux/fork.c
:
/* Reset the file list. These are recursive mutexes. */
fresetlockfiles ();
/* Reset locks in the I/O code. */
_IO_list_resetlock ();
/* Reset the lock the dynamic loader uses to protect its data. */
__rtld_lock_initialize (GL(dl_load_lock));
donde cada una de las funciones resetlock
y lock_initialize
finalmente llaman al equivalente interno de glibc de pthread_mutex_init()
, restableciendo efectivamente la resetlock
lock_initialize
independientemente de los camareros.
Creo que la teoría es que, al obtener el bloqueo (recursivo), se garantiza que ningún otro subproceso tocará las estructuras de datos (al menos de una manera que podría causar una falla), y luego restablecer los bloqueos individuales garantiza que los recursos no están t permanentemente bloqueado. (Restablecer el bloqueo del subproceso actual es seguro ya que ahora no hay otros subprocesos para competir por la estructura de datos, y de hecho no lo estará hasta que la función que está utilizando el bloqueo haya regresado).
No estoy 100% convencido de que esto cubra todas las eventualidades (sobre todo porque si / cuando el manejador de señales regresa, la función que acaba de robar su bloqueo intentará desbloquearlo, y la función de desbloqueo recursivo interno no protege contra el desbloqueo). demasiadas veces!) - pero parece que se podría construir un esquema funcional sobre bloqueos recursivos seguros para señales asíncronas.
Finalmente, incluso si la bifurcación es segura en los controladores de señales, ¿es seguro bifurcarla en un controlador de señales y luego regresar desde el controlador de señales, o si una llamada a un servidor de señales en un controlador de señales siempre necesita una llamada posterior a _exit o uno de los ejecutivos? ¿Familia de funciones antes de que regrese el manejador de señales?
¿Supongo que estás hablando sobre el proceso del niño? (Si fork()
es async-signal-safe significa algo, ¡entonces debería ser posible regresar en el padre!)
Al no haber encontrado nada en la especificación que indique lo contrario (aunque puede que lo haya perdido), creo que debería ser seguro; al menos, "seguro" en el sentido de que regresar del controlador de señales en el niño no implica un comportamiento indefinido en y por sí mismo, aunque el hecho de que un proceso de múltiples subprocesos se haya bifurcado puede implicar que exec*()
o _exit()
es probablemente el curso de acción más seguro.
Según entiendo de la fuente de la horquilla en glibc , utiliza la sección crítica del estado de la señal para asegurarse de que el procedimiento de bifurcación no se verá interrumpido por una señal.
ss = _hurd_self_sigstate ();
__spin_lock (&ss->critical_section_lock);
Y a medida que los manejadores pthread_atfork
ejecutan después del bloqueo de la sección crítica, se convierten automáticamente en señales seguras.
Puede ser que estoy equivocado en eso, apreciaré las correcciones.
Usar fork () en un manejador de señales debería estar bien.
pthread_atfork suena como una mala idea para usar.
Para responder a su pregunta original, pthread no puede garantizar que la seguridad de llamar a cualquier función pthread_atfork sea segura para async-signal porque la implementación de señales del kernel lo hace imposible.
Ah, y si ingresa el controlador de señales, no permita que el niño regrese del controlador de señales. Eso no está definido.