web-dev-qa-db-fra.com

Traitement des erreurs de segmentation

J'ai une application que j'utilise pour détecter tout défaut de segmentation ou ctrl-c. En utilisant le code ci-dessous, je peux attraper l'erreur de segmentation mais le gestionnaire est appelé encore et encore. Comment puis-je les arrêter. Pour votre information, je ne souhaite pas quitter mon application. Je peux juste prendre soin de libérer tous les tampons corrompus.

C'est 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 le gestionnaire va comme ça.

void mysighandler()
{
MyfreeBuffers(); /*related to my applciation*/
}

Ici, pour le signal de défaut de segmentation, le gestionnaire est appelé plusieurs fois et comme MyfreeBuffers () me donne des erreurs pour libérer de la mémoire déjà libérée. Je veux juste libérer une seule fois mais je ne veux toujours pas quitter l'application.

Veuillez aider.

32
user1225606

L'action par défaut pour des choses comme SIGSEGV est de terminer votre processus mais comme vous avez installé un gestionnaire pour cela, il appellera votre gestionnaire en remplaçant le comportement par défaut. Mais le problème est que l'instruction segfaulting peut être réessayée une fois que votre gestionnaire a terminé et si vous n'avez pas pris de mesures pour corriger la première erreur seg, l'instruction retried sera à nouveau en défaut et elle continuera encore et encore.

Donc, repérez d'abord l'instruction qui a abouti à SIGSEGV et essayez de la corriger (vous pouvez appeler quelque chose comme backtrace() dans le gestionnaire et voir par vous-même ce qui n'a pas fonctionné)

De plus, la norme POSIX dit que,

Le comportement d'un processus n'est pas défini après son retour normal d'une fonction de capture de signal pour un signal [XSI] SIGBUS, SIGFPE, SIGILL ou SIGSEGV qui n'a pas été généré par kill (), [RTS] sigqueue (), ou raise ( ).

Donc, la chose idéale à faire est de réparer votre erreur de segmentation en premier lieu. Le gestionnaire pour segfault n'est pas censé contourner la condition d'erreur sous-jacente

Donc, la meilleure suggestion serait- Ne pas attraper le SIGSEGV. Laissez-le vider le cœur. Analysez le noyau. Corrigez la référence de mémoire invalide et c'est parti!

34
Pavan Manjunath

Je ne suis pas du tout d'accord avec l'énoncé "Ne pas attraper le SIGSEGV" .

C'est une assez bonne pratique pour faire face aux conditions inattendues . Et c'est beaucoup plus propre pour faire face à NULL pointeurs (comme indiqué par les pannes malloc) avec un mécanisme de signal associé à setjmp/longjmp, que de distribuer la gestion des conditions d'erreur tout au long de votre code.

Notez cependant que si vous utilisez '' sigaction '' sur SEGV, vous ne devez pas oublier de dire SA_NODEFER dans sa_flags - ou trouver un autre moyen de gérer le fait que SEGV ne déclenchera votre gestionnaire qu'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;
}
10
Champignac

Si le SIGSEGV se déclenche à nouveau, la conclusion évidente est que l'appel à MyfreeBuffers(); a pas corrigé le problème sous-jacent (et si cette fonction ne fait vraiment que free() une certaine mémoire allouée, je ne sais pas pourquoi vous penseriez que ce serait).

En gros, un SIGSEGV se déclenche lors d'une tentative d'accès à une adresse mémoire inaccessible. Si vous n'allez pas quitter l'application, vous devez soit rendre cette adresse mémoire accessible, soit modifier le chemin d'exécution avec longjmp().

9
caf

Vous ne devriez pas essayer de continuer après SIG_SEGV. Cela signifie essentiellement que l'environnement de votre application est corrompu d'une manière ou d'une autre. Il se peut que vous veniez de déréférencer un pointeur nul, ou qu'un bug ait provoqué la corruption de votre pile, du tas ou d'une variable de pointeur, vous ne savez tout simplement pas. La chose seulement sûre à faire est de terminer le programme.

Il est parfaitement légitime de gérer le contrôle-C. De nombreuses applications le font, mais vous devez faire très attention à ce que vous faites dans votre gestionnaire de signaux. Vous ne pouvez appeler aucune fonction qui n'est pas réentrante. Cela signifie donc que si votre MyFreeBuffers() appelle la fonction stdlib free(), vous êtes probablement foutu. Si l'utilisateur frappe control-C alors que le programme est au milieu de malloc() ou free() et donc à mi-chemin de la manipulation des 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 signaux.

À propos de la seule chose sûre que vous pouvez faire dans un gestionnaire de signal est de définir un indicateur pour indiquer que vous avez capté le signal. Votre application peut ensuite interroger le drapeau à intervalles pour décider si elle doit effectuer une action.

6
JeremyP

On dirait qu'au moins sous Linux, l'utilisation de l'astuce avec l'option -fnon-call-exceptions peut être la solution. Il donnera la possibilité de convertir le signal en exception C++ générale et de le gérer de manière générale. Regardez les linux3/gcc46: "-fnon-call-exceptions", quels signaux sont des instructions de piégeage? par exemple.

0
Ivan Uskov

Je peux voir en cas de récupération à partir d'un SIG_SEGV, si votre gestion des événements dans une boucle et l'un de ces événements provoque une violation de segmentation, vous ne voudrez que sauter cet événement, continuer à traiter les événements restants. À mes yeux, SIG_SEGV est similaire à NullPointerException en Java. Oui, l'état sera incohérent et inconnu après l'un ou l'autre, mais dans certains cas, vous souhaitez gérer la situation et continuer. Par exemple, dans le trading Algo, vous suspendriez l'exécution d'un ordre et permettriez à un opérateur de prendre le relais manuellement, sans planter le système entier et ruiner tous les autres ordres.

0
newlogic

Eh bien, vous pouvez définir une variable d'état et ne libérer de la mémoire que si elle n'est pas définie. Le gestionnaire de signal sera appelé à chaque fois, vous ne pouvez pas contrôler cet AFAIK.

0
g13n