2011-03-12 36 views
19

He creado un pthread e instalado un controlador de señales dentro de él, del mismo modo que lo hacemos en la función main(). El manejador de señal del hilo es una función separada. Sorprendentemente, no está funcionando, es decir, el manejador de señal del hilo no puede captar señales.Manejo de señales en pthreads

Aquí está el código:

#include <pthread.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdio.h> 
#include <signal.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); 
} 

Salida:

Fallo de segmentación (que significa que la señal no está sujeta al manejador)

+7

Ha descubierto que [los hilos y las señales no interactúan bien] (http: // stackoverflow.com/questions/2575106/posix-threads-and-signals) :) – sarnold

+1

Para empezar, y para continuar lo que dice @sarnold, estás usando la API incorrecta. No use 'signal()'. Desde la página man (* read it *): "Los efectos de signal() en un proceso multiproceso no están especificados". Comience a leer documentos en el hombre 2 sigaction. – rlibby

+0

@rlibby: Entonces, ¿usaré la estructura "struct sigaction" o "sigevent" para captar las señales, quieres decir? – kingsmasher1

Respuesta

24

Hay varios problemas con su código:

  • ptr no es inicializado, por lo que todos los ptr-> partes se colgará el programa
  • que está llamando pthread_kill() de inmediato, es muy probable antes de que se haya instalado el manejador de señal, y en un hilo (que tiene un comportamiento no especificado)
  • llame al printf() desde un manejador de señal, que no se garantiza que funcione (consulte man 7 signal para obtener una lista de funciones seguras)

Esto funcionará mucho mejor, aunque todavía había necesidad de sincronización de hilos apropiado, y como se ha dicho en otro lugar, debe utilizar sigaction():

#include <pthread.h> 
#include <unistd.h> 
#include <sys/types.h> 
#include <stdio.h> 
#include <signal.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 sighandler en hilo principal

+0

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. – kingsmasher1

+0

También registra el manejador de señal en la función principal, en lugar de la función de hilo. ¿Por que es esto entonces? – kingsmasher1

+1

@kingsmasher: los dos problemas principales fueron la inicialización del puntero y el hecho de que usted llamó a 'pthread_kill()' antes de que el subproceso hijo tuviera tiempo de configurar el manejador de señal. Sin estas dos correcciones, no había forma de que su programa se ejecutara como se esperaba. Los otros problemas son problemas de portabilidad que causarán un comportamiento inesperado en varios sabores de Linux o Unix, por lo que también deberían abordarse. Hasta entonces, considere que solo funciona por casualidad. –

10

creo que el núcleo del problema es que las señales se entregan al proceso como un todo, en lugar de hilos individuales. Comúnmente, un solo hilo está nominado para manejar todas las señales; todos los demás hilos (incluido el hilo principal) necesitan block the signals using pthread_sigmask().

Puede configurar la máscara para que bloquee todas las señales, inicie el hilo del manejador de señal, desenmascare las señales que desea manejar y luego regrese al hilo principal, inicie todos los otros hilos que necesite. Ellos 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 estandarizada. (Y por tanto más fáciles de transportar.)

+0

@sarnold: Usted dijo: "cree que el núcleo del problema es que las señales se entregan al proceso como un todo, en lugar de hilos individuales", pero si utilizo pthread_kill (thread_id, signal_no) la señal que creo que se entrega a él específico hilo. – kingsmasher1

+0

@ Kingsmasher1, no estoy seguro de que sea cierto :) pero la respuesta de @Sam Hocevar es increíble. Estoy tentado de eliminar mi respuesta, pero podría ser útil. – sarnold

+0

@sarnold: Sí, funcionó demasiado :) Acaba de ejecutar su código. – kingsmasher1

4

El único problema con el código que nadie ha mencionado es que, aunque el bloqueo de señal (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 manejadores de señal de la persona que llama.

Tenga en cuenta también que el uso de manejadores de señales para la comunicación entre los hilos tiene un rendimiento por debajo del óptimo en comparación con otros métodos de hilo de señalización como variables de condición o barreras, porque hay al menos una transición de usuario-kernel-usuario adicional (cuando los rendimientos manejador de la señal)

+0

Gracias por la cuidadosa investigación y sus comentarios. Usted dijo "mientras que el bloqueo de señal (y la entrega, si usa pthread_kill o raise) son por subproceso, los manejadores de señal son por proceso". Si registramos el manejador de señal dentro del subproceso, ¿no cree que es con referencia a solo ese hilo? ¿Cuál es la diferencia entre agregar controladores de señal (para ser precisos, registrar manejadores de señal) dentro de la función principal, y dentro de los hilos? Sé que si nos registramos en main() también se ocupa de los hilos, ya que los hilos comparten el código main(). – kingsmasher1

+0

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

+0

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