2010-08-05 10 views
133

Quels sont les scénarios dans lesquels un processus obtient un SIGABRT en C++? Ce signal provient-il toujours du processus ou ce signal peut-il être envoyé d'un processus à un autre?Quand un processus obtient-il SIGABRT (signal 6)?

Existe-t-il un moyen d'identifier le processus qui envoie ce signal?

+2

Il existe plusieurs façons. Le plus simple, si vous avez écrit le programme, est d'enregistrer un gestionnaire de signal pour SIGABRT qui imprime ces informations et vide ses flux avant de revenir. Le deuxième moyen le plus simple consiste à exécuter le programme dans l'interface. Le troisième moyen le plus simple consiste à s'assurer que le programme génère un fichier de base lorsqu'il se bloque et de le découvrir via le vidage du noyau. –

Répondre

134

abort() envoie le processus appelant le signal SIGABRT, c'est ainsi que abort() fonctionne essentiellement.

abort() est généralement appelée par des fonctions de bibliothèque qui détectent une erreur interne ou une contrainte sérieuse. Par exemple malloc() appellera abort() si ses structures internes sont endommagées par un débordement de tas.

+14

pour moi dans la plupart des cas SIGABRT a été envoyé par 'libc' en essayant d'appeler' free() 'sur un pointeur non initialisé/corrompu – grandrew

+0

Si j'ai quelque part dans le code, enterré appel de fonction virtuelle pure depuis le constructeur, pourrait-il aussi finir avec le signal SIGABRT? Je demande si je vois une erreur indiquant que j'ai un appel virtuel pur, et la ligne suivante me donne un message SIGABRT et l'application se bloque ou est fermée par le système d'exploitation. Merci. – Hrvoje

44

Vous pouvez envoyer tout signal à tout processus en utilisant l'interface kill(2):

kill -SIGABRT 30823

30823 est un processus dash j'ai commencé, pour que je puisse trouver facilement le processus que je voulais tuer.

$ /bin/dash 
$ Aborted 

La sortie Aborted est apparemment comment dash fait état d'une SIGABRT.

Il peut être envoyé directement à tout processus en utilisant kill(2), ou un processus peut envoyer le signal à lui-même via assert(3), abort(3) ou raise(3).

37

SIGABRT est couramment utilisé par libc et d'autres bibliothèques pour annuler le programme en cas d'erreurs critiques. Par exemple, glibc envoie un SIGABRT en cas de détection de double corruption ou d'autres corruptions de tas.

En outre, la plupart des implémentations "assert" utilisent SIGABRT en cas de défaillance.

En outre, SIGABRT peut être envoyé à partir de n'importe quel autre processus comme n'importe quel autre signal. Bien sûr, le processus d'envoi doit fonctionner en tant que même utilisateur ou root.

12

Cela se produit généralement en cas de problème d'allocation de mémoire.

Il m'est arrivé quand mon programme essayait d'allouer un tableau avec une taille négative.

3

La GNU libc imprimera des informations à /dev/tty concernant certaines conditions fatales avant d'appeler abort() (qui déclenche alors SIGABRT), mais si vous exécutez votre programme en tant que service ou non pas dans une vraie fenêtre de terminal, ce message peut se perdre, car il n'y a pas de tty pour afficher les messages.

Voir mon post sur la réorientation libc écrire à stderr au lieu de/dev/TTY:

Catching libc error messages, redirecting from /dev/tty

6

Il y a une autre cause simple, en cas de C++.

std::thread::~thread{ 
    if((joinable()) 
     std::terminate(); 
} 

c.-à-d.la portée de fil terminé, mais vous avez oublié d'appeler soit

thread::join(); 

ou

thread::detach(); 
1

Dans mon cas, il était dû à une entrée dans un tableau à un indice égal à la longueur du tableau.

string x[5]; 

for(int i=1; i<=5; i++){ 

    cin>>x[i]; 

} 

x [5] est en cours d'accès qui n'est pas présent.

2

Un cas où le processus obtient SIGABRT de lui-même: Hrvoje mentionné à propos d'un être virtuel pur enterré appelé de ctor générant un abandon, j'ai recréé un exemple pour cela. Ici, lorsque d doit être construit, il appelle d'abord sa classe de base A ctor, et passe à l'intérieur du pointeur vers lui-même. A ctor appelle la méthode virtuelle pure avant que la table ait été remplie avec un pointeur valide, car d n'est pas encore construit.

#include<iostream> 
using namespace std; 
class A { 
public: 
A(A *pa){pa->f();} 
virtual void f()=0; 
}; 
class D : public A { 
public: 
D():A(this){} 
virtual void f() {cout<<"D::f\n";} 
}; 
int main(){ 
D d; 
A *pa = &d; 
pa->f(); 
return 0; 
} 

compilation: g ++ -o aa aa.cpp

ulimit -c reproductions illimité

: ./aa

pure virtual method called 
terminate called without an active exception 
Aborted (core dumped) 

Voyons maintenant rapidement le fichier de base, et valider que SIGABRT était en effet appelé:

gdb aa core 

voir regs:

i r 
rdx   0x6  6 
rsi   0x69a 1690 
rdi   0x69a 1690 
rip   0x7feae3170c37 

code de contrôle:

CEIQ 0x7feae3170c37

mov $0xea,%eax = 234 <- this is the kill syscall, sends signal to process 
syscall <----- 

http://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/

234 sys_tgkill pid_t tgid pid_t pid int sig = 6 = SIGABRT

:)