Manejo de señales en pthreads

9 minutos de lectura

avatar de usuario
Raj Sanpui

Creé un pthread e instalé un controlador de señal dentro de él, de la misma manera que lo hacemos en main( ) función. El controlador de señal del hilo es una función separada. Sorprendentemente, no funciona, es decir, el controlador de señales del subproceso no puede captar señales.

Aquí está el código:

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 printf("Caught signal: %d\n",sig);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 printf("This is from thread function\n");
 signal(SIGSEGV,sig_func); // Register signal handler inside thread
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data *ptr;

 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 printf("Name:%s\n",ptr->name);
 printf("Age:%d\n",ptr->age);
}

Producción:

Error de segmentación (lo que significa que el controlador no capta la señal)

  • Has descubierto que los hilos y las señales no interactúan bien 🙂

    – Sarnold

    12 de marzo de 2011 a las 11:09

  • Para empezar, y para continuar con lo que dijo @sarnold, estás usando la API incorrecta. No utilice signal(). Desde la página del manual (léelo): “Los efectos de la señal () en un proceso de subprocesos múltiples no están especificados”. Comience a leer documentos en man 2 sigaction.

    – rlibby

    12 de marzo de 2011 a las 11:16

  • @rlibby: Entonces, ¿debería usar la “estructura sigaction” o la “estructura sigevent” para captar las señales, quieres decir?

    – Raj Sanpui

    12 de marzo de 2011 a las 11:19

avatar de usuario
sam hocevar

Hay varios problemas con su código:

  • ptr no está inicializado, por lo que todos los ptr-> las partes bloquearán el programa
  • estas llamando pthread_kill() inmediatamente, muy probablemente antes de que se haya instalado el controlador de señal, y en un subproceso (que tiene un comportamiento no especificado)
  • llama printf() de un controlador de señal, que no se garantiza que funcione (ver man 7 signal para obtener una lista de funciones seguras)

Esto funcionará mucho mejor, aunque aún necesitaría una sincronización de subprocesos adecuada y, como se indicó en otra parte, debe usar sigaction():

#include <pthread.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdio.h>
#include <signal.h>
#include <string.h>

typedef struct data
{
 char name[10];
 int age;
}data;

void sig_func(int sig)
{
 write(1, "Caught signal 11\n", 17);
 signal(SIGSEGV,sig_func);
}

void func(data *p)
{
 fprintf(stderr, "This is from thread function\n");
 strcpy(p->name,"Mr. Linux");
 p->age=30;
 sleep(2); // Sleep to catch the signal
}

int main()
{
 pthread_t tid;
 pthread_attr_t attr;
 data d;
 data *ptr = &d;

 signal(SIGSEGV,sig_func); // Register signal handler before going multithread
 pthread_attr_init(&attr);
 pthread_create(&tid,&attr,(void*)func,ptr);
 sleep(1); // Leave time for initialisation
 pthread_kill(tid,SIGSEGV);

 pthread_join(tid,NULL);
 fprintf(stderr, "Name:%s\n",ptr->name);
 fprintf(stderr, "Age:%d\n",ptr->age);
}

Editar: instalar sighhandler en hilo principal

  • Muchas gracias, funcionó. Entonces, ¿crees que solo la inicialización del puntero fue un problema? Pero si no entrego ninguna señal, el código funciona sin errores.

    – Raj Sanpui

    12 de marzo de 2011 a las 11:30

  • También funciona el registro del controlador de señal en el principal, en lugar de la función de subproceso. ¿Por que es esto entonces?

    – Raj Sanpui

    12 de marzo de 2011 a las 11:31

  • @kingsmasher: los dos problemas principales fueron la inicialización del puntero y el hecho de que llamaste pthread_kill() antes de que el subproceso secundario tuviera tiempo de configurar el controlador de señal. Sin estas dos correcciones, no había forma de que su programa pudiera ejecutarse como se esperaba. Los otros problemas son problemas de portabilidad que causarán un comportamiento inesperado en varios tipos de Linux o Unix, por lo que también deben abordarse. Hasta entonces, considera que solo funciona por casualidad.

    – sam hocevar

    12 de marzo de 2011 a las 11:40

  • También vale la pena señalar: mi página man para signal() dice “Los efectos de signal() en un proceso de subprocesos múltiples no están especificados”. Y recomienda usar sigaction() así como.

    – Julien-L

    09/04/2013 a las 23:22

  • ptr no está inicializado, por lo que todos los ptr-> las partes bloquearán el programa absolutamente falso, realmente depende de muchas cosas, pero ciertamente causará comportamiento indefinido.

    – Iharob Al Asimi

    02/09/2015 a las 16:02


Creo que el núcleo del problema es que las señales se entregan al proceso como un todo, en lugar de subprocesos individuales. Por lo general, se designa un solo subproceso para manejar todas las señales; todos los demás subprocesos (incluido el subproceso principal) deben bloquear las señales usando pthread_sigmask().

Puede configurar la máscara para bloquear todas las señales, iniciar su subproceso de manejo de señales, desenmascarar las señales que desea manejar y luego, de regreso en el subproceso principal, iniciar todos los demás subprocesos que necesite. Heredarán la máscara “bloquear todas las señales” del hilo principal.

Por cierto, es hora de alejarse de signal(3) y cambiar a sigaction(2), que tiene una semántica confiable y está mejor estandarizado. (Y por lo tanto más portátil.)

  • @sarnold: Dijiste: “Creo que el núcleo del problema es que las señales se entregan al proceso como un todo, en lugar de subprocesos individuales”, pero si uso pthread_kill (thread_id, signal_no), creo que la señal se entrega al subproceso específico. .

    – Raj Sanpui

    12 de marzo de 2011 a las 11:23

  • @ Kingsmasher1, no estoy seguro de que eso sea cierto 🙂 pero la respuesta de @ Sam Hocevar se ve increíble. Estoy tentado a eliminar mi respuesta, pero aún podría ser útil.

    – Sarnold

    12 de marzo de 2011 a las 11:26

  • @sarnold: las señales se entregan al proceso como un todo, pero según el pthread_kill() documentación, asegura que el subproceso esperado recibirá la señal si la señal no es SIGSTOP, SIGCONT o SIGTERM.

    – sam hocevar

    12 de marzo de 2011 a las 11:32

  • @Sam Hocevar, todos los documentos que afirman que provienen de las páginas de manual de POSIX, que son maravillosos, pero no confío en que informen sobre la realidad de las API de subprocesamiento de Linux. 🙁 Si tienes documentos de la gente de NPTL que dicen lo mismo, eso realmente alegraría mi día. 🙂

    – Sarnold

    12 de marzo de 2011 a las 11:35

  • NPTL afirma cumplir con POSIX, y lo hace bastante bien. No he encontrado ningún problema evidente de incumplimiento, excepto quizás algunas cosas relacionadas con la insistencia del núcleo en tener grupos y grupos reales/efectivos/guardados/fs uid/gid separados por subproceso, y obligar al espacio de usuario a sincronizar cambiándolos a todos. (NPTL no sincroniza los cambios en los grupos complementarios a pesar de que POSIX indica que los grupos son una propiedad del proceso, no del subproceso).

    – R.. GitHub DEJA DE AYUDAR A ICE

    12 de marzo de 2011 a las 13:36

El único problema con su código que nadie ha mencionado todavía es que, mientras que el bloqueo de señales (y la entrega, si usa pthread_kill o raise) son por subproceso, los controladores de señal son por proceso. Esto significa que son un mecanismo muy malo para la comunicación entre subprocesos, especialmente si su código alguna vez se usará como código de biblioteca, ya que es un comportamiento extremadamente malo para una biblioteca alterar los controladores de señal de la persona que llama.

También tenga en cuenta que el uso de controladores de señales para la comunicación entre subprocesos tiene un rendimiento subóptimo en comparación con otros métodos de señalización de subprocesos, como variables de condición o barreras, porque hay al menos una transición adicional de usuario-núcleo-usuario (cuando el controlador de señales regresa).

  • Gracias por la cuidadosa investigación y sus comentarios. Dijiste “mientras que el bloqueo de señales (y la entrega, si usas pthread_kill o raise) son por hilo, los controladores de señales son por proceso”. Si registramos el controlador de señales dentro del hilo, ¿no crees que es con referencia? a solo ese hilo? ¿Cuál es la diferencia entre agregar controladores de señal (para ser precisos, registrar controladores de señal) dentro de la función principal e hilos internos? Sé que si nos registramos dentro de main() también se ocupa de los subprocesos, ya que los subprocesos comparten el código principal().

    – Raj Sanpui

    13 de marzo de 2011 a las 10:34


  • Entonces, si registramos el controlador dentro de la función del hilo, ¿es solo específico para ese hilo?

    – Raj Sanpui

    13 de marzo de 2011 a las 10:38


  • @kingsmahser1: no, nada indica que el almacenamiento para signal es o debería ser subproceso local. Debe suponer que registrar un controlador de señal tiene el mismo efecto sea cual sea el hilo actual.

    – sam hocevar

    13/03/2011 a las 20:31

  • Como dijo Sam, los manejadores de señales son proceso global. No puede instalar controladores de señal por subproceso. Podría intentar ser inteligente e instalar un controlador de señal que lea un puntero de función con pthread_getspecific como el controlador específico del subproceso para la señal, excepto que pthread_getspecific no es seguro para señales asíncronas. Hasta donde yo sé, no hay una forma segura de señal asíncrona para determinar en qué subproceso se encuentra, por lo que el único punto de enviar una señal a un subproceso específico es generar EINTR (si SA_RESTART se omite de sigaction banderas) o detener el progreso de ese hilo…

    – R.. GitHub DEJA DE AYUDAR A ICE

    14 de marzo de 2011 a las 2:19

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad