Obsługa sygnałów z wieloma wątkami w systemie Linux


119

Co się dzieje w systemie Linux, gdy program (który może mieć wiele wątków) odbiera sygnał, taki jak SIGTERM lub SIGHUP?

Który wątek przechwytuje sygnał? Czy wiele wątków może otrzymać ten sam sygnał? Czy istnieje specjalny wątek poświęcony wyłącznie obsłudze sygnałów? Jeśli nie, co dzieje się wewnątrz wątku, który ma obsłużyć sygnał? Jak wznawia się wykonywanie po zakończeniu procedury obsługi sygnału?

Odpowiedzi:


35

Jest to nieco zniuansowane, w zależności od używanej wersji jądra Linuksa.

Zakładając wątki 2.6 posix, a jeśli mówimy o systemie operacyjnym wysyłającym SIGTERM lub SIGHUP, sygnał jest wysyłany do procesu, który jest odbierany i obsługiwany przez wątek główny. Używając wątków POSIX, możesz również wysyłać SIGTERM do poszczególnych wątków, ale podejrzewam, że pytasz o to, co się stanie, gdy system operacyjny wyśle ​​sygnał do procesu.

W 2.6, SIGTERM spowoduje "czyste" wyjście z wątków potomnych, podczas gdy jako 2.4 wątki potomne pozostały w stanie nieokreślonym.


A co dzieje się wewnątrz wątku głównego po odebraniu sygnału? Powiedzmy, że napisałem niestandardowy program obsługi sygnału dla SIGUSR1 i teraz wysyłam ten sygnał do procesu. Wątek główny otrzyma ten sygnał. Może w tym momencie jest w środku jakiejś funkcji. Co się stanie?

1
jeśli masz konfigurację obsługi, będzie ona traktowana jako przerwanie, a przepływ programu zostanie zatrzymany, a Twój niestandardowy program obsługi zostanie wykonany. Po wykonaniu sterowanie powróci, zakładając, że nie zrobiłeś nic, aby zmienić normalny przepływ (wyjście itp.).
Alan

Zauważ, że jest to specyficzne dla SIGUSR1, którego IIRC nie przerywa wywołań systemowych. Jeśli spróbujesz tego na przykład z SIGINT, może to przerwać odczyt strumienia, a kiedy powrócisz do czytania, strumień może zwrócić błąd, że został przerwany.
Alan,

10
Jestem trochę zdezorientowany, co oznacza „wątek główny”. Czy to oznacza, że ​​program obsługi dla SIGTERM będzie zawsze działał w głównym wątku, czy też może działać w dowolnym wątku?
Stephen Nutt

3
Ta odpowiedź , która stwierdza, że ​​do obsługi sygnału został wybrany dowolny wątek, zaprzecza Twojej odpowiedzi.
user202729

135

pthreads(7) opisuje, że POSIX.1 wymaga wszystkich wątków w atrybutach udziału procesu, w tym:

  • dyspozycje sygnałowe

POSIX.1 wymaga również, aby niektóre atrybuty były odrębne dla każdego wątku, w tym:

Procedura jądra Linuksa complete_signalma następujący blok kodu - komentarze są bardzo przydatne:

/*
 * Now find a thread we can wake up to take the signal off the queue.
 *
 * If the main thread wants the signal, it gets first crack.
 * Probably the least surprising to the average bear.
 */
if (wants_signal(sig, p))
        t = p;
else if (!group || thread_group_empty(p))
        /*
         * There is just one thread and it does not need to be woken.
         * It will dequeue unblocked signals before it runs again.
         */
        return;
else {
        /*
         * Otherwise try to find a suitable thread.
         */
        t = signal->curr_target;
        while (!wants_signal(sig, t)) {
                t = next_thread(t);
                if (t == signal->curr_target)
                        /*
                         * No thread needs to be woken.
                         * Any eligible threads will see
                         * the signal in the queue soon.
                         */
                        return;
        }
        signal->curr_target = t;
}

/*
 * Found a killable thread.  If the signal will be fatal,
 * then start taking the whole group down immediately.
 */
if (sig_fatal(p, sig) &&
    !(signal->flags & SIGNAL_GROUP_EXIT) &&
    !sigismember(&t->real_blocked, sig) &&
    (sig == SIGKILL || !p->ptrace)) {
        /*
         * This signal will be fatal to the whole group.
         */

Więc widać, że ty jesteś odpowiedzialny za gdzie sygnały są dostarczane:

Jeśli twój proces ustawił dyspozycję sygnału na SIG_IGNlub SIG_DFL, sygnał jest ignorowany (lub domyślnie - kill, core lub ignore) dla wszystkich wątków.

Jeśli twój proces ustawił dyspozycję sygnału na określoną procedurę obsługi, możesz kontrolować, który wątek otrzyma sygnały, manipulując określonymi maskami sygnału wątku za pomocą pthread_sigmask(3). Możesz wyznaczyć jeden wątek do zarządzania nimi wszystkimi lub utworzyć jeden wątek na sygnał lub dowolną mieszankę tych opcji dla określonych sygnałów, lub polegać na bieżącym domyślnym zachowaniu jądra Linuksa polegającym na dostarczaniu sygnału do głównego wątku.

Jednak niektóre sygnały są specjalne według strony podręcznika signal(7):

Sygnał może być generowany (a zatem oczekujący) dla procesu jako całości (np., Gdy jest wysyłany przy użyciu funkcji kill (2) ) lub dla określonego wątku (np. Pewne sygnały, takie jak SIGSEGV i SIGFPE, generowane w wyniku wykonania określone instrukcje języka maszynowego są skierowane do wątków, podobnie jak sygnały kierowane do określonego wątku za pomocą pthread_kill (3) ). Sygnał skierowany na proces może być dostarczony do dowolnego z wątków, w których sygnał aktualnie nie jest zablokowany. Jeśli więcej niż jeden wątek ma odblokowany sygnał, to jądro wybiera dowolny wątek, do którego ma dostarczyć sygnał.

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.