Traitement des défauts de Segmentation
j'ai une application que j'utilise pour attraper tout défaut de segmentation ou ctrl-C. En utilisant le code ci-dessous, je suis capable d'attraper la faille de segmentation mais le handler est appelé encore et encore. Comment puis-je les arrêter. Pour ton information, je ne veux pas quitter ma candidature. Je peux juste prendre soin de libérer tous les tampons corrompus.
est-ce possible?
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);
}
et handler fait comme ça.
void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}
Ici pour le signal de défaut de Segmentation, handler est appelé plusieurs fois et comme myfreebuffers évident() me donne des erreurs pour libérer la mémoire déjà libérée. Je veux seulement libre seule fois, mais encore ne veulent pas quitter l'application.
Aidez-moi.
7 réponses
l'action par défaut pour des choses comme SIGSEGV
est de mettre fin à votre processus, mais comme vous avez installé un gestionnaire pour cela, il appellera votre gestionnaire en surpassant le comportement par défaut. Mais le problème est que l'instruction segfaulting peut être reprise après la fin de votre handler et si vous n'avez pas pris de mesures pour corriger la première erreur seg, l'instruction reprise sera de nouveau défaut et il va et continue.
donc premier spot l'instruction qui a abouti à SIGSEGV
et essayer pour le réparer (vous pouvez appeler quelque chose comme backtrace()
dans le handler et voir par vous-même ce qui a mal tourné)
aussi, la norme POSIX dit que,
le comportement d'un processus n'est pas défini après qu'il retourne normalement de une fonction de capture de signal pour un [XSI] SIGBUS, SIGFPE, SIGILL, ou Signal SIGSEGV qui n'a pas été généré par kill(), [RTS] sigqueue(), ou raise().
Donc, l'idéal la chose à faire est de réparer votre segfault en premier lieu. Handler pour segfault n'est pas destiné à contourner la condition d'erreur sous-jacente
donc la meilleure suggestion serait - ne pas attraper le SIGSEGV
. Laissez dump de base. Analysez le noyau. Fixez la référence de mémoire invalide et voilà!
si le SIGSEGV
se déclenche à nouveau, la conclusion évidente est que l'appel à MyfreeBuffers();
a et non a corrigé le problème sous-jacent (et si cette fonction ne fait vraiment free()
qu'une partie de la mémoire allouée, Je ne sais pas pourquoi vous pensez qu'il le ferait).
en gros, un SIGSEGV
se déclenche lorsqu'on tente d'accéder à une adresse mémoire inaccessible. Si vous n'allez pas quitter l'application, vous devez soit faire que de la mémoire adresse accessible, ou changer le chemin d'exécution avec longjmp()
.
Je ne suis pas du tout d'accord avec l'affirmation " N'attrapez pas le SIGSEGV " .
C'est une très bonne pratique pour faire face à conditions inattendues . Et c'est beaucoup plus propre pour faire face aux pointeurs NULL (comme donné par les échecs malloc) avec le mécanisme de signal associé à setjmp/longjmp
, que de distribuer la gestion de l'état d'erreur tout au long de votre code.
noter cependant que si vous utilisez "sigaction "sur SEGV
, vous ne devez pas oublier de dire SA_NODEFER
dans sa_flags
- ou de trouver une autre façon de traiter le fait SEGV
déclenchera votre gestionnaire une seule fois.
#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;
}
vous ne devriez pas essayer de continuer après SIG_SEGV
. Cela signifie essentiellement que l'environnement de votre application est endommagé de quelque façon. Il se peut que vous ayez déréférencé un pointeur null, ou qu'un bug ait causé la corruption de votre programme dans sa pile ou le tas ou une variable pointeur, vous ne le savez pas. La seulement chose sûre à faire est de mettre fin au programme.
il est parfaitement légitime de manipuler contrôle-C. Beaucoup d'applications le font, mais vous devez être vraiment prudent exactement ce que vous faites dans votre gestionnaire de signal. Vous ne pouvez pas appeler une fonction qui n'est pas rentrée. Donc, si votre MyFreeBuffers()
appelle la fonction free()
de stdlib, vous êtes probablement fichu. Si l'utilisateur tape control-C pendant que le programme est au milieu de malloc()
ou free()
et donc à mi - chemin en manipulant les structures de données qu'ils utilisent pour suivre les allocations de tas, vous serez presque certainement corrompre le tas si vous appelez malloc()
ou free()
dans le gestionnaire de signal.
à propos de la seule chose sûre que vous pouvez faire dans un manipulateur de signal est de mettre un drapeau pour dire que vous avez capté le signal. Votre application peut alors afficher le drapeau à intervalles pour décider si elle a besoin d'effectuer une certaine action.
vous pouvez définir une variable d'état et seulement la mémoire libre si elle n'est pas définie. Le gestionnaire de signal sera appelé à chaque fois, vous ne pouvez pas contrôler cet AFAIK.
je peux voir à case pour récupérer D'un SIG_SEGV, si votre manipulation d'événements dans une boucle et l'un de ces événements provoque une Violation de Segmentation alors vous ne voudriez sauter sur cet événement, continuer à traiter les événements restants. À mes yeux, SIG_SEGV ressemble à NullPointerException en Java. Oui l'état est incompatible et inconnu après l'un de ces, toutefois, dans certains cas, vous souhaitez gérer la situation et de poursuivre. Par exemple, dans Algo trading vous interrompre l'exécution d'un ordre et permettre à un trader de prendre la relève manuellement, sans planter tout le système et ruiner tous les autres ordres.
ressemble au moins sous Linux en utilisant l'option trick with-fnon-call-exceptions peut être la solution. Il donnera la possibilité de convertir le signal à l'exception générale C++ et de le gérer de manière générale. Regardez le linux3 / gcc46: "-fnon-call-exceptions", quels signaux sont des instructions de piégeage? par exemple.