socioeconomica - Manejo de fallas de segmentación.
tipos de segmentacion de clientes (7)
Tengo una aplicación que utilizo para detectar cualquier falla de segmentación o ctrl-c. Al usar el código de abajo, puedo detectar el error de segmentación pero se llama al manejador una y otra vez. ¿Cómo puedo detenerlos? Para su información, no quiero salir de mi aplicación. Solo puedo encargarme de liberar todos los buffers corruptos.
¿Es posible?
void SignalInit(void )
{
struct sigaction sigIntHandler;
sigIntHandler.sa_handler = mysighandler;
sigemptyset(&sigIntHandler.sa_mask);
sigIntHandler.sa_flags = 0;
sigaction(SIGINT, &sigIntHandler, NULL);
sigaction(SIGSEGV, &sigIntHandler, NULL);
}
y el manejador va así.
void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}
Aquí para la señal de falla de Segmentación, se llama al controlador varias veces y, como es obvio, MyfreeBuffers () me da errores para liberar la memoria ya liberada. Solo quiero liberar una vez, pero todavía no quiero salir de la aplicación.
Por favor ayuda.
Bueno, podrías establecer una variable de estado y solo liberar memoria si no está configurada. Se llamará al manejador de señales cada vez que no pueda controlar ese AFAIK.
La acción predeterminada para cosas como SIGSEGV
es terminar su proceso, pero como ha instalado un controlador, llamará a su controlador anulando el comportamiento predeterminado. Pero el problema es que la instrucción de incumplimiento puede reintentarse después de que su manejador finalice y, si no ha tomado medidas para solucionar el primer fallo de seguridad, la instrucción reintentada volverá a fallar y continúa.
Así que primero localice la instrucción que resultó en SIGSEGV
e intente arreglarla (puede llamar a algo como backtrace()
en el controlador y ver por sí mismo lo que salió mal)
Además, el estándar POSIX dice que,
El comportamiento de un proceso no está definido después de que retorna normalmente de una función de captura de señal para una señal [XSI] SIGBUS, SIGFPE, SIGILL o SIGSEGV que no fue generada por kill (), [RTS] sigqueue (), o raise ( ).
Por lo tanto, lo ideal es corregir su falta de seguridad en primer lugar. El controlador para segfault no pretende omitir la condición de error subyacente
Así que la mejor sugerencia sería : No captar el SIGSEGV
. Dejarlo volcar el núcleo. Analizar el núcleo. ¡Arregla la referencia de memoria inválida y listo!
No debes intentar continuar después de SIG_SEGV
. Básicamente significa que el entorno de su aplicación está dañado de alguna manera. Podría ser que acabas de eliminar la referencia de un puntero nulo, o podría ser que algún error haya causado que tu programa dañe su pila o el montón o alguna variable de puntero, simplemente no lo sabes. Lo único seguro es terminar el programa.
Es perfectamente legítimo manejar el control-C. Muchas aplicaciones lo hacen, pero tienes que tener mucho cuidado exactamente con lo que haces en tu controlador de señales. No puedes llamar a ninguna función que no sea reingresante. Así que eso significa que si MyFreeBuffers()
llama a la función stdlib free()
, es probable que estés jodido. Si el usuario presiona control-C mientras el programa se encuentra en medio de malloc()
o free()
y, por lo tanto, a la mitad de la manipulación de las estructuras de datos que usan para rastrear las asignaciones de almacenamiento dinámico, es casi seguro que corromperá el montón si llama a malloc()
o free()
en el manejador de señal.
Lo único seguro que puede hacer en un controlador de señales es establecer una marca para indicar que ha captado la señal. Luego, su aplicación puede sondear la bandera a intervalos para decidir si necesita realizar alguna acción.
No estoy de acuerdo en absoluto con la declaración "No atrapar el SIGSEGV" .
Esa es una práctica bastante buena para lidiar con condiciones inesperadas . Y eso es mucho más limpio para hacer frente a los punteros NULOS (como lo indican las fallas de malloc) con un mecanismo de señal asociado a setjmp/longjmp
, que distribuir la gestión de las condiciones de error a lo largo de su código.
Sin embargo, tenga en cuenta que si usa '''' sigaction '''' en SEGV
, no debe olvidar decir SA_NODEFER
en sa_flags
, o busque otra forma de lidiar con el hecho de que SEGV
activará su controlador solo una vez.
#include <setjmp.h>
#include <signal.h>
#include <stdio.h>
#include <string.h>
static void do_segv()
{
int *segv;
segv = 0; /* malloc(a_huge_amount); */
*segv = 1;
}
sigjmp_buf point;
static void handler(int sig, siginfo_t *dont_care, void *dont_care_either)
{
longjmp(point, 1);
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sigaction));
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_NODEFER;
sa.sa_sigaction = handler;
sigaction(SIGSEGV, &sa, NULL); /* ignore whether it works or not */
if (setjmp(point) == 0)
do_segv();
else
fprintf(stderr, "rather unexpected error/n");
return 0;
}
Parece que al menos en Linux el truco con la opción -fnon-call-exceptions puede ser la solución. Dará la posibilidad de convertir la señal a una excepción general de C ++ y manejarla de manera general. Mire linux3 / gcc46: "-fnon-call-exceptions", ¿qué señales son instrucciones de captura? por ejemplo.
Puedo ver en el caso de recuperarse de un SIG_SEGV, si sus eventos de manejo en un bucle y uno de estos eventos causa una violación de la Segmentación, entonces solo querrá saltear este evento, continuar procesando los eventos restantes. En mi opinión, SIG_SEGV es similar a la NullPointerException en Java. Sí, el estado será inconsistente y desconocido después de cualquiera de estos, sin embargo, en algunos casos, le gustaría manejar la situación y continuar. Por ejemplo, en el comercio de Algo, se detendría la ejecución de una orden y se permitiría a un comerciante hacerse cargo manualmente, sin bloquear todo el sistema y arruinando todas las demás órdenes.
Si el SIGSEGV
dispara nuevamente, la conclusión obvia es que la llamada a MyfreeBuffers();
no ha solucionado el problema subyacente (y si esa función realmente solo free()
alguna memoria asignada, no estoy seguro de por qué pensaría que lo haría).
Aproximadamente, un SIGSEGV
dispara cuando se intenta acceder a una dirección de memoria inaccesible. Si no va a salir de la aplicación, debe hacer que la dirección de la memoria sea accesible o cambiar la ruta de ejecución con longjmp()
.