sigint c
ejecutando el manejador de seƱal predeterminado (4)
He escrito una aplicación en la que he registrado el número de manejadores de señales para diferentes señales en linux. Después de que el proceso recibe la señal, el control se transfiere al manejador de señal que tenía registrado. En este manejador de señales, hago un trabajo que necesito hacer, y luego me gustaría llamar al SIF_DFL
señales predeterminado, es decir, SIF_DFL
o SIG_IGN
. Sin embargo, SIG_DFL
y SIG_ING
son macros que se expanden a valores numéricos 0 y 1 respectivamente, que son direcciones de función no válidas.
¿Hay alguna manera de que pueda llamar a las acciones predeterminadas, es decir, SIG_DFL
o SIG_IGN
?
Para lograr el efecto de SIG_DFL
o SIG_ING
llamo exit (1) y no hago nada, respectivamente. Pero para señales como SIGSEGV
también me gustaría tener un volcado de núcleo. En general, me gustaría que mi comportamiento predeterminado sea el mismo que SIG_DFL
e ignorar el mismo comportamiento SIG_IGN
, de la misma manera que lo haría el sistema operativo.
Dado que los manejadores de señales están implementados en el kernel, la única manera que veo es
- restablecer el controlador y
-
raise()
la señal de nuevo
El enfoque habitual es restablecer el manejador de señales y luego raise()
la señal nuevamente:
Aquí hay un ejemplo de controlador SIGINT:
void sigint_handler(int num)
{
/* handle SIGINT */
// call default handler
signal(SIGINT, SIG_DFL);
raise(SIGINT);
}
Puede guardar el controlador anterior y luego llamarlo cuando sea el momento adecuado.
Instalar el controlador. Asegúrate de guardar el controlador antiguo
static struct sigaction new_sa, old_sa;
new_sa.handler = my_handler;
sigemptyset(&new_handler.sa_mask);
if (sigaction(signo, &new_sa, &old_sa) == -1) {
/* handle sigaction error */
}
En su nuevo controlador, llame al controlador antiguo
(*old_sa.sa_handler)(signo)
No necesitas levantarlo de nuevo o hacer cualquier cosa desordenada; simplemente llame al controlador anterior (por supuesto, ya que guardó la sigaction
que tiene acceso a la disposición anterior y así sucesivamente).
El Manual de referencia de la biblioteca GNU C tiene todo un capítulo que explica todo sobre el manejo de señales.
Siempre se obtiene el controlador de señales establecido previamente (un puntero de función) cuando instala su propio controlador (consulte las páginas de manual de signal()
o sigaction()
).
previous_handler = signal(SIGINT, myhandler);
La regla general es que siempre puede restablecer el controlador anterior y raise()
la señal nuevamente.
void myhandler(int sig) {
/* own stuff .. */
signal(sig, previous_handler);
raise(sig);
/* when it returns here .. set our signal handler again */
signal(sig, myhandler);
}
Hay una desventaja de la regla general: las excepciones de hardware que se asignan a señales generalmente se asignan a una determinada instrucción que causó la excepción. Por lo tanto, cuando vuelve a emitir una señal, la instrucción asociada no es la misma que la original. Esto puede pero no debe dañar a otros manejadores de señales.
Otra desventaja es que cada señal elevada causa mucho tiempo de procesamiento. Para evitar el uso excesivo de raise()
puede utilizar las siguientes alternativas:
En el caso de
SIG_DFL
el puntero a la función apunta a la dirección0
(que obviamente no es una dirección válida). Por lo tanto, debe restablecer el controlador yraise()
la señal nuevamente.if (previous_handler == SIG_DFL) { signal(sig, SIG_DFL); raise(sig); signal(sig, myhandler); }
SIG_IGN
tiene valor1
(también una dirección no válida). Aquí solo puedes volver (no hacer nada).else if (previous_handler == SIG_IGN) { return; }
De lo contrario (ni
SIG_IGN
niSIG_DFL
) ha recibido un puntero de función válido y puede llamar al controlador directamente,else { previous_handler(sig); }
Por supuesto, también debe considerar las diferentes API (consulte las páginas de manual de signal()
y sigaction()
).