Pregunta

Si por algún milagro ocurre un error de segmentación en nuestro programa, quiero capturar el SIGSEGV y hacerle saber al usuario (posiblemente un cliente GUI) con un único código de retorno que ha ocurrido un problema grave.Al mismo tiempo, me gustaría mostrar información en la línea de comando para mostrar qué señal se captó.

Hoy nuestro manejador de señales tiene el siguiente aspecto:

void catchSignal (int reason) {
  std :: cerr << "Caught a signal: " << reason << std::endl;
  exit (1);
}

Puedo escuchar los gritos de horror con lo anterior, como he leído en este hilo que es malo llamar a una función no reentrante desde un manejador de señales.

¿Existe una forma portátil de manejar la señal y brindar información a los usuarios?

EDITAR: ¿O al menos portátil dentro del marco POSIX?

¿Fue útil?

Solución

Este mesa enumera todas las funciones que POSIX garantiza que son seguras para señales asíncronas y, por lo tanto, pueden llamarse desde un controlador de señales.

Al utilizar el comando 'escribir' de esta tabla, se espera que la siguiente solución relativamente "fea" funcione:

#include <csignal>

#ifdef _WINDOWS_
#define _exit _Exit
#else
#include <unistd.h>
#endif

#define PRINT_SIGNAL(X) case X: \
          write (STDERR_FILENO, #X ")\n" , sizeof(#X ")\n")-1); \
          break;

void catchSignal (int reason) {
  char s[] = "Caught signal: (";
  write (STDERR_FILENO, s, sizeof(s) - 1);
  switch (reason)
  {
    // These are the handlers that we catch
    PRINT_SIGNAL(SIGUSR1);
    PRINT_SIGNAL(SIGHUP);
    PRINT_SIGNAL(SIGINT);
    PRINT_SIGNAL(SIGQUIT);
    PRINT_SIGNAL(SIGABRT);
    PRINT_SIGNAL(SIGILL);
    PRINT_SIGNAL(SIGFPE);
    PRINT_SIGNAL(SIGBUS);
    PRINT_SIGNAL(SIGSEGV);
    PRINT_SIGNAL(SIGTERM);
  }

  _Exit (1);  // 'exit' is not async-signal-safe
}

EDITAR: Construyendo sobre ventanas.

Después de intentar crear esta ventana, parece que 'STDERR_FILENO' no está definido.Sin embargo, según la documentación, su valor parece ser '2'.

#include <io.h>
#define STDIO_FILENO 2

EDITAR: ¡Tampoco se debe llamar a 'exit' desde el controlador de señales!

Como lo señala burbujeante, llamar a _Exit en lo anterior es un enfoque de mazo para señales como HUP y TERM.Idealmente, cuando se detectan estas señales, se puede usar una bandera con el tipo "volatile sig_atomic_t" para notificar al programa principal que debe salir.

Lo siguiente me resultó útil en mis búsquedas.

  1. Introducción a la programación de señales Unix
  2. Ampliando las señales tradicionales

Otros consejos

FWIW, 2 también es un error estándar en Windows, pero necesitarás alguna compilación condicional porque su escritura() se llama _write().También querrás

#ifdef SIGUSR1 /* or whatever */

etc. en torno a todas las referencias a señales que no se garantiza que estén definidas por el estándar C.

Además, como se indicó anteriormente, no desea manejar SIGUSR1, SIGHUP, SIGINT, SIGQUIT y SIGTERM de esta manera.

Richard, todavía no hay suficiente karma para comentar, así que me temo que hay una nueva respuesta.Éstas son señales asincrónicas;No tiene idea de cuándo se entregan, por lo que posiblemente esté en el código de la biblioteca que debe completarse para mantener la coherencia.Por lo tanto, es necesario que regresen los manejadores de señales para estas señales.Si llama a exit(), la biblioteca realizará algún trabajo después de main(), incluida la llamada a funciones registradas con atexit() y la limpieza de los flujos estándar.Este procesamiento puede fallar si, por ejemplo, su señal llegó a una función de E/S de biblioteca estándar.Por lo tanto, en C90 no se le permite llamar a exit().Veo que ahora C99 relaja el requisito al proporcionar una nueva función _Exit() en stdlib.h._Exit() se puede llamar de forma segura desde un controlador para una señal asincrónica._Exit() no llamará a funciones atexit() y puede omitir la limpieza de los flujos estándar a discreción de la implementación.

Para bk1e (comentarista algunas publicaciones más arriba)El hecho de que SIGSEGV sea síncrono es la razón por la que no se pueden utilizar funciones que no estén diseñadas para ser reentrantes.¿Qué pasa si la función que falló tenía un bloqueo y la función llamada por el controlador de señales intenta adquirir el mismo bloqueo?

Esta es una posibilidad, pero el problema no es "el hecho de que SIGSEGV sea síncrono".Llamar a funciones no reentrantes desde el controlador es mucho peor con señales asincrónicas por dos razones:

  • Los manejadores de señal asincrónicos (generalmente) esperan regresar y reanudar la ejecución normal del programa.Un controlador para una señal sincrónica va a terminar de todos modos, por lo que no ha perdido mucho si se bloquea.
  • En un sentido perverso, usted tiene control absoluto sobre cuándo se entrega una señal síncrona: esto sucede cuando ejecuta su código defectuoso y en ningún otro momento.No tienes ningún control sobre cuándo se entrega una señal asíncrona.A menos que el propio código de E/S del OP sea la causa del defecto (p. ej.generando un carácter incorrecto*: su mensaje de error tiene una posibilidad razonable de tener éxito.

Escriba un programa de inicio para ejecutar su programa e informar el código de salida anormal al usuario.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top