Manejo de señales con múltiples hilos en Linux

En Linux, ¿qué sucede cuando un progtwig (que posiblemente tiene varios hilos) recibe una señal, como SIGTERM o SIGHUP?

¿Qué hilo intercepta la señal? ¿Pueden varios hilos obtener la misma señal? ¿Hay un hilo especial dedicado exclusivamente al manejo de señales? Si no, ¿qué sucede dentro del hilo que debe manejar la señal? ¿Cómo se reanuda la ejecución después de que finaliza la rutina del manejador de señal?

Esto se matiza ligeramente, según la versión del kernel de Linux que esté utilizando.

Suponiendo 2.6 subprocesos de posix, y si está hablando del sistema operativo que envía SIGTERM o SIGHUP, la señal se envía al proceso, que es recibido y manejado por el subproceso raíz. Usando hilos POSIX, también puede enviar SIGTERM a hilos individuales, pero sospecho que está preguntando qué sucede cuando el sistema operativo envía la señal al proceso.

En 2.6, SIGTERM provocará que los subprocesos hijos salgan “limpiamente”, donde como 2.4, los subprocesos secundarios quedaron en un estado indeterminado.

pthreads(7) describe que POSIX.1 requiere todos los subprocesos en un atributo de recurso compartido de proceso, que incluye:

  • disposiciones de señal

POSIX.1 también requiere que algunos atributos sean distintos para cada subproceso, incluidos:

  • máscara de señal ( pthread_sigmask(3) )

  • stack de señal alternativa ( sigaltstack(2) )

La rutina complete_signal() del kernel de Linux tiene el siguiente bloque de código: los comentarios son bastante útiles:

  /* * Now find a thread we can wake up to take the signal off the queue. * * If the main thread wants the signal, it gets first crack. * Probably the least surprising to the average bear. */ if (wants_signal(sig, p)) t = p; else if (!group || thread_group_empty(p)) /* * There is just one thread and it does not need to be woken. * It will dequeue unblocked signals before it runs again. */ return; else { /* * Otherwise try to find a suitable thread. */ t = signal->curr_target; while (!wants_signal(sig, t)) { t = next_thread(t); if (t == signal->curr_target) /* * No thread needs to be woken. * Any eligible threads will see * the signal in the queue soon. */ return; } signal->curr_target = t; } /* * Found a killable thread. If the signal will be fatal, * then start taking the whole group down immediately. */ if (sig_fatal(p, sig) && !(signal->flags & (SIGNAL_UNKILLABLE | SIGNAL_GROUP_EXIT)) && !sigismember(&t->real_blocked, sig) && (sig == SIGKILL || !t->ptrace)) { /* * This signal will be fatal to the whole group. */ 

Entonces, ves que estás a cargo de donde se entregan las señales:

Si su proceso ha establecido una disposición de señal para SIG_IGN o SIG_DFL , entonces la señal se ignora (o por defecto – kill, core o ignore) para todos los hilos.

Si su proceso ha establecido una disposición de señal para una rutina de controlador específica, entonces puede controlar qué hilo recibirá las señales manipulando máscaras de señal de subproceso específicas usando pthread_sigmask(3) . Puede nominar un hilo para gestionarlos todos, o crear un hilo por señal, o cualquier combinación de estas opciones para señales específicas, o puede confiar en el comportamiento predeterminado actual del kernel de Linux de entregar la señal al hilo principal.

Algunas señales, sin embargo, son especiales:

  A signal may be generated (and thus pending) for a process as a whole (eg, when sent using kill(2)) or for a specific thread (eg, certain signals, such as SIGSEGV and SIGFPE, generated as a consequence of executing a specific machine- language instruction are thread directed, as are signals targeted at a specific thread using pthread_kill(3)). A process-directed signal may be delivered to any one of the threads that does not currently have the signal blocked. If more than one of the threads has the signal unblocked, then the kernel chooses an arbitrary thread to which to deliver the signal.