2011-05-16 21 views
15

He escrito una aplicación donde tengo un número registrado de manejador de señal para diferentes señales en linux. Después de que el proceso recibe la señal, el control se transfiere al controlador de señal que había registrado. En este manejador de señal hago un trabajo que debo hacer, y luego me gustaría llamar al manejador de señal por defecto, es decir, SIF_DFL o SIG_IGN. Sin embargo, SIG_DFL y SIG_ING son macros que se expanden a los valores numéricos 0 y 1 respectivamente, que son direcciones de función no válidas.ejecución del manejador de señal predeterminado

¿Hay alguna manera en que pueda llamar acciones predeterminadas, es decir, SIG_DFL o SIG_IGN?

Para lograr el efecto de SIG_DFL o SIG_ING llamo a la salida (1) y no hago nada, respectivamente. Pero para señales como SIGSEGV también me gustaría tener un volcado del núcleo. En general, me gustaría que mi comportamiento predeterminado fuera el mismo que SIG_DFL e ignorar el mismo comportamiento SIG_IGN, de la misma manera que lo haría el sistema operativo.

+0

posible duplicación de [Invocar explícitamente SIG \ _DFL/SIG \ _IGN controladores en Linux] (http://stackoverflow.com/questions/3147840/explícitamente-invocar-sig-dfl-sig-ign-handlers-on-linux) – pilcrow

Respuesta

2

Dados los manejadores de señales están implementados en kernel, la única manera que veo es a

  • restablecer el manejador y
  • raise() de nuevo la señal
4

El método usual es restablecer el manejador de señal y luego raise() la señal de nuevo:

Aquí hay un ejemplo de controlador SIGINT:

void sigint_handler(int num) 
{ 
    /* handle SIGINT */ 

    // call default handler 
    signal(SIGINT, SIG_DFL); 
    raise(SIGINT); 
} 
+0

¿Pero cómo solo invocar el controlador predeterminado sin cambiar el controlador? En su código 'sigint_handler' ya no se usará como manejador' SIGINT'. Y no hay un punto claro donde volver a establecerlo en 'sigint_handler'. –

+0

@ AdamBadura - Bueno, el controlador predeterminado para SIGINT dará por finalizado el proceso. El hecho de que el proceso ya no se ejecutará después de que se ejecute el manejador predeterminado hace que la restauración del manejador de señal sea un poco discutible. – CubicleSoft

+0

@CubicleSoft Tienes razón en ese caso. Pero no en general cuando no sabes qué manejador predeterminado. Mientras que la pregunta original no parece estar limitada a 'SIGINT'. –

9

Puede guardar el controlador anterior y luego llamarlo cuando sea el momento adecuado.

Instalar el controlador. Asegúrese de guardar 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, llamar al controlador antiguo

(*old_sa.sa_handler)(signo) 

No es necesario volver a subir o hacer cualquier cosa sucia; simplemente llame al controlador anterior (por supuesto, ya que guardó el sigaction tiene acceso a la disposición anterior, etc.).

+0

¿No necesitarías también mirar el bit 'SA_SIGINFO' en' sa_flags', y llamar a 'sa_sigaction' o a' sa_handler'? –

+0

@Greg Hewgill Usted debería :-) – cnicutar

+0

Hoy estoy aprendiendo más de lo que pensaba que necesitaba saber sobre el manejo de señales de Linux (vea http://stackoverflow.com/questions/6935988/how-to-provide-extend-on -write-functional-for-memory-mapped-files-in-linux). –

13

The GNU C Library Reference Manual tiene un capítulo completo que explica todo sobre el manejo de señales.

Se trata siempre del establecido previamente manejador de la señal (un puntero de función) al instalar su propio controlador (vea las páginas de manual para signal() o sigaction()).

previous_handler = signal(SIGINT, myhandler); 

La regla general es, que siempre se puede restablecer al controlador anterior y raise() la señal de nuevo.

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 norma general: las excepciones de hardware que se asignan a las señales se suelen asignar a una determinada instrucción que provocó la excepción. Por lo tanto, cuando vuelve a generar una señal, la instrucción asociada no es la misma que originalmente. Esto puede pero no debería dañar a otros manejadores de señal.

Otra desventaja de es que cada señal elevada causa mucho tiempo de procesamiento. Para evitar el uso excesivo de raise() puede utilizar las siguientes alternativas:

  1. En caso de SIG_DFL el puntero de función para abordar 0 (que obviamente no hay dirección válida). Por lo tanto, usted tiene que reiniciar el controlador y raise() la señal de nuevo.

    if (previous_handler == SIG_DFL) 
    { 
        signal(sig, SIG_DFL); 
        raise(sig); 
        signal(sig, myhandler); 
    }
  2. SIG_IGN tiene valor 1 (también una dirección no válida). Aquí puedes simplemente regresar (no hacer nada).

    else if (previous_handler == SIG_IGN) 
    { 
        return; 
    }
  3. De otro modo (ni SIG_IGN ni SIG_DFL) que han recibido un puntero de función válido y que puede llamar al manejador de forma directa,

    else 
    { 
        previous_handler(sig); 
    }

Por supuesto, usted tiene que considerar el diferentes API también (consulte las páginas de manual para signal() y sigaction()).

+0

Me gustaría considerar si vale la pena optimizar haciendo una llamada directa al controlador. Esto no es "a prueba de futuro". Si llega un sistema que ofrecerá otro manejador especial ('SIG_FUN'), la optimización anterior fracasará, ya que tratará de llamarlo realmente como función mientras no sea un puntero válido ... –

+7

¿Está seguro de que esto funcionará?La GNU C Library Reference establece (por ejemplo en 24.7.5) que la entrega de una señal se bloquea mientras se ejecuta un controlador para esa señal. Entonces su 'raise' solo enviará la señal pero no invocará el controlador. Luego reiniciará el manejador. Después de que exista su controlador, se enviará la señal de 'raise' pero encontrará su propio controlador. –

+0

El enlace que proporcionó está muerto. –

Cuestiones relacionadas