Kiedy proces otrzymuje SIGABRT (sygnał 6)?


202

Jakie są scenariusze, w których proces otrzymuje SIGABRT w C ++? Czy ten sygnał zawsze pochodzi z procesu, czy może on być przesyłany z jednego procesu do drugiego?

Czy istnieje sposób na określenie, który proces wysyła ten sygnał?


3
Istnieje kilka sposobów. Najłatwiejszym sposobem, jeśli napisałeś program, jest zarejestrowanie procedury obsługi sygnału dla SIGABRT, która drukuje te informacje i opróżnia strumienie przed powrotem. Drugim najprostszym sposobem jest uruchomienie programu w ramach strace. Trzecim najłatwiejszym sposobem jest upewnienie się, że program generuje plik podstawowy, gdy ulega awarii, i sprawdzenie go za pomocą zrzutu pamięci.
Parthian Shot

Odpowiedzi:


195

abort()wysyła SIGABRTsygnał wywołania , tak w abort()zasadzie działa.

abort()jest zwykle wywoływany przez funkcje biblioteczne, które wykrywają błąd wewnętrzny lub niektóre poważnie zerwane ograniczenie. Na przykład malloc()zadzwoni, abort()jeśli jego wewnętrzne struktury zostaną uszkodzone przez przepełnienie sterty.


27
dla mnie w większości przypadków SIGABRT został wysłany przez libcpróbę wywołania free()niezainicjalizowanego / uszkodzonego wskaźnika
grand17

Jeśli mam gdzieś w kodzie zakopane czysto wirtualne wywołanie funkcji z poziomu konstruktora, czy to może skończyć się sygnałem SIGABRT? Pytam, gdy widzę błąd informujący, że mam czystą wirtualną rozmowę, a następny wiersz wyświetla mi komunikat SIGABRT, a aplikacja ulega awarii lub zostaje zamknięta przez system operacyjny. Dzięki.
Hrvoje,

2
Na MacOS mamy SIGABRT do otwierania około 1000 uchwytów plików bez ich zamykania. Zamiast kpienia nasze testy wyodrębniły plik bardziej ogólnym typem czytnika, który nie ma Close()metody, więc został zapomniany. Miałem świetny zasięg. : rolleyes:
Zyl

51

SIGABRTjest powszechnie używany przez libc i inne biblioteki do przerwania programu w przypadku krytycznych błędów. Na przykład glibc wysyła komunikat SIGABRTw przypadku wykrycia podwójnego zwolnienia lub innych uszkodzeń sterty.

Ponadto większość assertimplementacji wykorzystuje SIGABRTw przypadku nieudanego potwierdzenia.

Ponadto SIGABRTmoże być wysyłany z dowolnego innego procesu, jak każdy inny sygnał. Oczywiście proces wysyłania musi przebiegać jako ten sam użytkownik lub root.


49

Możesz wysłać dowolny sygnał do dowolnego procesu za pomocą kill(2)interfejsu:

kill -SIGABRT 30823

30823 to dashproces, który rozpocząłem, więc mogłem łatwo znaleźć proces, który chciałem zabić.

$ /bin/dash
$ Aborted

Wynik Abortedjest najwyraźniej jak dashzgłasza SIGABRT.

To może być wysłany bezpośrednio do dowolnego procesu korzystania kill(2)lub proces może wysłać sygnał do samego siebie poprzez assert(3), abort(3)lub raise(3).


17

Zwykle dzieje się tak, gdy występuje problem z alokacją pamięci.

Zdarzyło mi się, gdy mój program próbował przydzielić tablicę o rozmiarze ujemnym.


14

Jest jeszcze jedna prosta przyczyna w przypadku c ++.

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

tzn. zakres wątku zakończył się, ale zapomniałeś zadzwonić

thread::join();

lub

thread::detach();

7

GNU libc wydrukuje informacje /dev/ttydotyczące niektórych krytycznych warunków przed wywołaniem abort()(które następnie SIGABRTsię uruchomi), ale jeśli uruchamiasz swój program jako usługę lub w inny sposób nie w prawdziwym oknie terminala, wiadomość ta może się zgubić, ponieważ nie ma tty, aby wyświetlić wiadomości.

Zobacz mój post na temat przekierowywania libc do pisania do stderr zamiast / dev / tty:

Łapanie komunikatów o błędach libc, przekierowywanie z / dev / tty


4

W przypadku, gdy proces pobiera SIGABRT od siebie: Hrvoje wspomniał o zakopanej czystej wirtualnej istocie wywoływanej z ctor generującej przerwanie, odtworzyłem przykład tego. Tutaj, kiedy ma zostać zbudowane d, najpierw wywołuje swoją podstawową klasę ctor i przekazuje do siebie wskaźnik wewnętrzny. ctor wywołuje czystą wirtualną metodę, zanim tabela została wypełniona prawidłowym wskaźnikiem, ponieważ d nie jest jeszcze skonstruowany.

#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;
}

skompiluj: g ++ -o aa aa.cpp

ulimit -c nieograniczony

uruchom: ./aa

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

teraz pozwala szybko zobaczyć podstawowy plik i sprawdzić, czy SIGABRT rzeczywiście został nazwany:

gdb aa core

zobacz regs:

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

Sprawdź kod:

disas 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

:)


2

W moim przypadku było to spowodowane wejściem do tablicy o indeksie równym długości tablicy.

string x[5];

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

    cin>>x[i];

}

x [5] jest dostępny, który nie jest obecny.


1

Jak trafnie zauważył „@sarnold”, każdy proces może wysłać sygnał do dowolnego innego procesu, dlatego jeden proces może wysłać SIGABORT do innego procesu iw takim przypadku proces odbiorczy nie jest w stanie odróżnić, czy nadchodzi z powodu własnej modyfikacji pamięć itp., lub ktoś inny ma „monstrualnie”, wyślij do niej.

W jednym z systemów, w których pracowałem, jest jeden detektor impasu, który faktycznie wykrywa, czy proces wychodzi z jakiegoś zadania, dając bicie serca, czy nie. Jeśli nie, oznacza to, że proces znajduje się w stanie zakleszczenia i wysyła do niego SIGABORT.

Chciałem tylko podzielić się tym prospektywnym w odniesieniu do zadanego pytania.


0

Dam odpowiedź z perspektywy programowania konkurencyjnego (cp) , ale dotyczy to również innych domen.

Wiele razy podczas wykonywania cp ograniczenia są dość duże.

Na przykład : miałem pytanie z N, M, Qtakimi zmiennymi , że 1 ≤ N, M, Q < 10^5.

Pomyłka robiłem ja ogłoszony 2D całkowitą tablicy wielkości 10000 x 10000w C++i zmagał się z SIGABRTbłędem w Codechef przez prawie 2 dni.

Teraz, jeśli obliczymy:

Typowy rozmiar liczby całkowitej: 4 bajty

Liczba komórek w naszej tablicy: 10000 x 10000

Całkowity rozmiar (w bajtach): 400000000 bajtów = 4 * 10 ^ 8 ≈ 400 MB

Twoje rozwiązania takich pytań będą działać na twoim komputerze (nie zawsze), ponieważ można sobie na to pozwolić.

Ale zasoby w witrynach kodujących (sędziowie online) są ograniczone do kilku KB.

Stąd SIGABRTbłąd i inne tego rodzaju błędy.

Wniosek:

W takich pytaniach nie powinniśmy deklarować tablicy ani wektora ani żadnego innego DS tej wielkości, ale naszym zadaniem jest uczynienie naszego algorytmu tak wydajnym, aby działał bez nich (DS) lub z mniejszą ilością pamięci.

PS : Mogą istnieć inne przyczyny tego błędu; powyżej był jednym z nich.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.