Jak SIGINT odnosi się do innych sygnałów zakończenia, takich jak SIGTERM, SIGQUIT i SIGKILL?

103

W systemach POSIX sygnały kończące mają zwykle następującą kolejność (zgodnie z wieloma stronami MAN i specyfikacją POSIX):

  1. SIGTERM - uprzejmie poproś o zakończenie procesu. Zakończy się z wdzięcznością, czyszcząc wszystkie zasoby (pliki, gniazda, procesy potomne itp.), Usuwając pliki tymczasowe i tak dalej.

  2. SIGQUIT - silniejsza prośba. Zakończy bez gracji, nadal czyści zasoby, które absolutnie wymagają oczyszczenia, ale może nie usunie plików tymczasowych, może zapisuje gdzieś informacje debugowania; w niektórych systemach zostanie również zapisany zrzut rdzenia (niezależnie od tego, czy sygnał zostanie przechwycony przez aplikację, czy nie).

  3. SIGKILL - najsilniejsza prośba. Proces nie jest nawet proszony o zrobienie czegokolwiek, ale system oczyści proces, czy tak będzie, czy nie. Najprawdopodobniej zapisywany jest zrzut pamięci.

Jak SIGINT pasuje do tego obrazu? Proces CLI jest zwykle przerywany przez SIGINT, gdy użytkownik trafi na CRTL + C, jednak proces w tle może być również zakończony przez SIGINT przy użyciu narzędzia KILL. To, czego nie widzę w specyfikacjach lub plikach nagłówkowych, to to, czy SIGINT jest mniej lub bardziej skuteczny niż SIGTERM lub czy w ogóle istnieje różnica między SIGINT i SIGTERM.

AKTUALIZACJA:

Najlepszy opis sygnałów kończących, jaki do tej pory znalazłem, znajduje się w dokumentacji GNU LibC . Wyjaśnia bardzo dobrze, że istnieje zamierzona różnica między SIGTERM a SIGQUIT.

Mówi o SIGTERM:

To normalny sposób, aby uprzejmie poprosić program o zakończenie.

I mówi o SIGQUIT:

[...] i tworzy zrzut pamięci po zakończeniu procesu, tak jak sygnał błędu programu. Można o tym myśleć jako o stanie błędu programu „wykrytym” przez użytkownika. [...] Podczas obsługi SIGQUIT najlepiej pomijać pewne rodzaje porządków. Na przykład, jeśli program tworzy pliki tymczasowe, powinien obsłużyć inne żądania zakończenia, usuwając pliki tymczasowe. Ale lepiej, aby SIGQUIT ich nie usuwał, aby użytkownik mógł je sprawdzić w połączeniu ze zrzutem jądra.

SIGHUP jest również wystarczająco dobrze wyjaśniony. SIGHUP nie jest tak naprawdę sygnałem zakończenia, oznacza po prostu "utratę połączenia" z użytkownikiem, więc aplikacja nie może oczekiwać, że użytkownik odczyta dalsze dane wyjściowe (np. Wyjście stdout / stderr) i nie ma żadnych danych wejściowych, których można oczekiwać od użytkownika już. W przypadku większości aplikacji oznacza to, że lepiej zakończyć. Teoretycznie aplikacja może również zdecydować, że przechodzi w tryb demona po odebraniu SIGHUP i działa teraz jako proces w tle, zapisując dane wyjściowe do skonfigurowanego pliku dziennika. W przypadku większości demonów, które już działają w tle, SIGHUP zwykle oznacza, że ​​powinny ponownie przeanalizować swoje pliki konfiguracyjne, więc po edycji plików konfiguracyjnych należy wysłać je do procesów w tle.

Jednak na tej stronie nie ma żadnego użytecznego wyjaśnienia SIGINT, poza tym, że jest on wysyłany przez CRTL + C. Czy jest jakiś powód, dla którego ktoś miałby traktować SIGINT inaczej niż SIGTERM? Jeśli tak, jaki byłby to powód i jak zmieniłaby się obsługa?

Mecki
źródło
1
Dobre pytanie. Podejrzewam, że w odpowiedź zaangażowana byłaby część historycznego okrucieństwa Uniksa.
Alex B
Wiem, że to pytanie jest stare, ale na wypadek, gdyby ktoś przyszedł tutaj po obszerną listę sygnałów dla twojego (Linux) systemu operacyjnego, można je znaleźć, wpisując sudo fuser -lw wierszu poleceń. Dla mnie to wywołuje:HUP INT QUIT ILL TRAP ABRT IOT BUS FPE KILL USR1 SEGV USR2 PIPE ALRM TERM STKFLT CHLD CONT STOP TSTP TTIN TTOU URG XCPU XFSZ VTALRM PROF WINCH IO PWR SYS UNUSED
Nick Bull
3
Spróbuj również kill -luzyskać listę sygnałów.
AAAfarmclub

Odpowiedzi:

88

SIGTERM i SIGKILL są przeznaczone do ogólnych żądań „zakończenia tego procesu”. SIGTERM (domyślnie) i SIGKILL (zawsze) spowodują zakończenie procesu. SIGTERM może zostać przechwycony przez proces (np. Aby mógł wykonać własne czyszczenie, jeśli chce) lub nawet całkowicie zignorowany; ale SIGKILL nie może zostać złapany ani zignorowany.

SIGINT i SIGQUIT są przeznaczone specjalnie dla żądań z terminala: określone znaki wejściowe mogą być przypisane do generowania tych sygnałów (w zależności od ustawień sterowania terminalu). Domyślną akcją dla SIGINT jest ten sam rodzaj zakończenia procesu, co domyślna akcja dla SIGTERM i niezmienna akcja dla SIGKILL; domyślną akcją dla SIGQUIT jest również zakończenie procesu, ale mogą wystąpić dodatkowe działania zdefiniowane w ramach implementacji, takie jak generowanie zrzutu pamięci. W razie potrzeby proces może je złapać lub zignorować.

SIGHUP, jak powiedziałeś, ma na celu wskazanie, że połączenie terminala zostało utracone, a nie być sygnałem zakończenia jako takim. Ale znowu, domyślną akcją dla SIGHUP (jeśli proces go nie wychwytuje lub zignoruje) jest zakończenie procesu w taki sam sposób jak SIGTERM itp.

W definicjach POSIX znajduje się tabela, signal.hktóra zawiera listę różnych sygnałów oraz ich domyślnych działań i celów, a rozdział Ogólny interfejs terminala zawiera dużo więcej szczegółów na temat sygnałów związanych z terminalami.

Matthew Slattery
źródło
10
To jest kluczowy punkt: SIGINT i SIGQUIT mogą być generowane z terminala przy użyciu pojedynczych znaków, podczas gdy program jest uruchomiony. Pozostałe sygnały muszą w jakiś sposób zostać wygenerowane przez inny program (np. Przez polecenie kill). SIGINT jest mniej brutalny niż SIGQUIT; ta ostatnia tworzy zrzut rdzenia. SIGKILL nie może zostać uwięziony. SIGHUP jest generowany w przypadku zawieszenia połączenia (zamknięcie okna itp.). Tak więc wszystkie mają różne znaczenia.
Jonathan Leffler
TTY Demystified zawiera łatwo przyswajalne informacje na temat interakcji sygnałów z podsystemem tty jądra. Dodaje kontekst do Twojej odpowiedzi.
Daniel Näslund
2
Wygląda na to, że specyfikacje nie są zbyt precyzyjne, ale rozumiem, że SIgint prosi program o zatrzymanie bieżącej akcji, niekoniecznie zakończenie. W przypadku programu zaprojektowanego tylko do wykonania jednej akcji na przebieg, co oznacza zakończenie, ale w przypadku programu interaktywnego, który ma jakąś pętlę odczytu, oceny i wydruku, może to oznaczać rezygnację z bieżącego eval i powrót do czytania danych wejściowych użytkownika. Myślę, że mniej robi to na sigint.
bdsl
Jaki sygnał jest wysyłany podczas restartu?
Dan Dascalescu
10

Jak zauważył DarkDust, wiele sygnałów daje te same wyniki, ale procesy mogą przypisywać im różne działania, rozróżniając sposób generowania każdego sygnału. Patrząc na kod źródłowy jądra FreeBSD (kern_sig.c) widzę, że oba sygnały są obsługiwane w ten sam sposób, przerywają proces i są dostarczane do dowolnego wątku.

SA_KILL|SA_PROC,             /* SIGINT */
SA_KILL|SA_PROC,             /* SIGTERM */
Diomidis Spinellis
źródło
9

man 7 signal

Jest to wygodna, nienormatywna strona podręcznika projektu Linux man-pages , na której często chcesz szukać informacji o sygnałach Linuksa.

Wersja 3.22 wspomina o interesujących rzeczach, takich jak:

Sygnały SIGKILL i SIGSTOP nie mogą zostać przechwycone, zablokowane ani zignorowane.

i zawiera tabelę:

Signal     Value     Action   Comment
----------------------------------------------------------------------
SIGHUP        1       Term    Hangup detected on controlling terminal
                              or death of controlling process
SIGINT        2       Term    Interrupt from keyboard
SIGQUIT       3       Core    Quit from keyboard
SIGILL        4       Core    Illegal Instruction
SIGABRT       6       Core    Abort signal from abort(3)
SIGFPE        8       Core    Floating point exception
SIGKILL       9       Term    Kill signal
SIGSEGV      11       Core    Invalid memory reference
SIGPIPE      13       Term    Broken pipe: write to pipe with no
                              readers
SIGALRM      14       Term    Timer signal from alarm(2)
SIGTERM      15       Term    Termination signal
SIGUSR1   30,10,16    Term    User-defined signal 1
SIGUSR2   31,12,17    Term    User-defined signal 2
SIGCHLD   20,17,18    Ign     Child stopped or terminated
SIGCONT   19,18,25    Cont    Continue if stopped
SIGSTOP   17,19,23    Stop    Stop process
SIGTSTP   18,20,24    Stop    Stop typed at tty
SIGTTIN   21,21,26    Stop    tty input for background process
SIGTTOU   22,22,27    Stop    tty output for background process

która podsumowuje sygnał, Actionktóry odróżnia np. SIGQUIT od SIGQUIT, ponieważ SIGQUIT ma akcję Corei SIGINT Term.

Działania są udokumentowane w tym samym dokumencie:

The entries in the "Action" column of the tables below specify the default disposition for each signal, as follows:

Term   Default action is to terminate the process.

Ign    Default action is to ignore the signal.
Core   Default action is to terminate the process and dump core (see core(5)).
Stop   Default action is to stop the process.
Cont   Default action is to continue the process if it is currently stopped.

Nie widzę żadnej różnicy między SIGTERM i SIGINT z punktu widzenia jądra, ponieważ oba mają akcję Termi można je złapać. Wydaje się, że jest to tylko „powszechne rozróżnienie według konwencji użytkowania”:

  • SIGINT jest tym, co się dzieje, gdy wykonujesz CTRL-C z terminala
  • SIGTERM jest domyślnym sygnałem wysyłanym przez kill

Niektóre sygnały są zgodne z ANSI C, a inne nie

Istotna różnica polega na tym, że:

  • SIGINT i SIGTERM to ANSI C, dzięki czemu są bardziej przenośne
  • SIGQUIT i SIGKILL nie są

Są one opisane w sekcji „7.14 Obsługa sygnałów” projektu C99 N1256 :

  • SIGINT odbiór interaktywnego sygnału uwagi
  • SIGTERM żądanie zakończenia wysłane do programu

co sprawia, że ​​SIGINT jest dobrym kandydatem do interaktywnego Ctrl + C.

POSIX 7

POSIX 7 dokumentuje sygnały z signal.hnagłówkiem: https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/signal.h.html

Ta strona zawiera również następującą tabelę zainteresowań, która wymienia niektóre rzeczy, które już widzieliśmy man 7 signal:

Signal    Default Action   Description
SIGABRT   A                Process abort signal.
SIGALRM   T                Alarm clock.
SIGBUS    A                Access to an undefined portion of a memory object.
SIGCHLD   I                Child process terminated, stopped,
SIGCONT   C                Continue executing, if stopped.
SIGFPE    A                Erroneous arithmetic operation.
SIGHUP    T                Hangup.
SIGILL    A                Illegal instruction.
SIGINT    T                Terminal interrupt signal.
SIGKILL   T                Kill (cannot be caught or ignored).
SIGPIPE   T                Write on a pipe with no one to read it.
SIGQUIT   A                Terminal quit signal.
SIGSEGV   A                Invalid memory reference.
SIGSTOP   S                Stop executing (cannot be caught or ignored).
SIGTERM   T                Termination signal.
SIGTSTP   S                Terminal stop signal.
SIGTTIN   S                Background process attempting read.
SIGTTOU   S                Background process attempting write.
SIGUSR1   T                User-defined signal 1.
SIGUSR2   T                User-defined signal 2.
SIGTRAP   A                Trace/breakpoint trap.
SIGURG    I                High bandwidth data is available at a socket.
SIGXCPU   A                CPU time limit exceeded.
SIGXFSZ   A                File size limit exceeded.

BusyBox init

Domyślne rebootpolecenie BusyBox 1.29.2 wysyła SIGTERM do procesów, usypia na sekundę, a następnie wysyła SIGKILL. Wydaje się, że jest to powszechna konwencja w różnych dystrybucjach.

Gdy zamykasz system BusyBox za pomocą:

reboot

wysyła sygnał do procesu init.

Następnie program obsługi sygnału inicjującego kończy wywołanie:

static void run_shutdown_and_kill_processes(void)
{
    /* Run everything to be run at "shutdown".  This is done _prior_
     * to killing everything, in case people wish to use scripts to
     * shut things down gracefully... */
    run_actions(SHUTDOWN);

    message(L_CONSOLE | L_LOG, "The system is going down NOW!");

    /* Send signals to every process _except_ pid 1 */
    kill(-1, SIGTERM);
    message(L_CONSOLE, "Sent SIG%s to all processes", "TERM");
    sync();
    sleep(1);

    kill(-1, SIGKILL);
    message(L_CONSOLE, "Sent SIG%s to all processes", "KILL");
    sync();
    /*sleep(1); - callers take care about making a pause */
}

który drukuje na terminal:

The system is going down NOW!
Sent SIGTERM to all processes
Sent SIGKILL to all processes

Oto minimalny, konkretny przykład .

Sygnały wysyłane przez jądro

Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
źródło
8

Po szybkim wyszukiwaniu w Google sigint vs sigterm wygląda na to, że jedyną zamierzoną różnicą między nimi jest to, czy zostało zainicjowane przez skrót klawiaturowy, czy przez jawne wywołanie kill.

W rezultacie możesz na przykład przechwycić sigint i zrobić z nim coś specjalnego, wiedząc, że prawdopodobnie został wysłany przez skrót klawiaturowy. Może odświeży ekran lub coś w tym stylu, zamiast umierać (niezalecane, ponieważ ludzie oczekują ^Czabicia programu, tylko przykład).

Dowiedziałem się też, że ^\należy wysłać sigquit, którego sam mogę zacząć używać. Wygląda na bardzo przydatne.

Jonathan
źródło
3

Używając kill(zarówno wywołania systemowego, jak i narzędzia, możesz wysłać prawie każdy sygnał do dowolnego procesu, pod warunkiem, że masz pozwolenie. Proces nie może rozróżnić, jak powstał sygnał i kto go wysłał.

To powiedziawszy, SIGINT naprawdę ma zasygnalizować przerwanie Ctrl-C, podczas gdy SIGTERM jest ogólnym sygnałem końcowym. Nie ma koncepcji, że sygnał byłby "silniejszy", z jedynym wyjątkiem, że istnieją sygnały, których nie można zablokować ani obsłużyć (SIGKILL i SIGSTOP, zgodnie ze stroną podręcznika).

Sygnał może być tylko „silniejszy” niż inny sygnał w odniesieniu do tego, jak proces odbierający obsługuje sygnał (i jakie jest domyślne działanie dla tego sygnału). Na przykład, domyślnie zarówno SIGTERM, jak i SIGINT prowadzą do zakończenia. Ale jeśli zignorujesz SIGTERM, nie zakończy on twojego procesu, podczas gdy SIGINT nadal to robi.

DarkDust
źródło
0

Z wyjątkiem kilku sygnałów, programy obsługi sygnałów mogą przechwytywać różne sygnały lub można zmodyfikować domyślne zachowanie po otrzymaniu sygnału. Zobacz signal(7)stronę podręcznika, aby uzyskać szczegółowe informacje.

Ignacio Vazquez-Abrams
źródło
Tak, ale jeśli nie znam "znaczenia" singla, łapanie sygnału jest bezcelowe, bo nie będę wiedział, co robić w mojej aplikacji. GNU C np. Wyjaśnia różnicę między SIGQUIT i SIGTERM. Ale zawiera niewiele informacji o SIGINT: gnu.org/s/libc/manual/html_node/Termination-Signals.html
Mecki,
"Sygnał SIGINT(" przerwanie programu ") jest wysyłany, gdy użytkownik wpisze znak INTR (normalnie C-c)." „SIGINT 2 Term Interrupt from keyboard” Naciśnięcie Ctrl-C, SIGINTzostanie wysłane. Proces zwykle umiera. Co więcej dać?
Ignacio Vazquez-Abrams
Potencjalnie może istnieć opis zamiaru, który programista powinien założyć, że użytkownik będzie miał, gdy naciśnie ctrl-c, czyli semantyka wiadomości. Na przykład specyfikacja HTTP jasno określa, że ​​gdy użytkownik wysyła żądanie GET, serwer nie powinien zakładać, że chce, aby zrobił cokolwiek poza odesłaniem odpowiedzi.
bdsl