Ogólnie rzecz biorąc, na kończenie procesów możemy generować sygnały takie jak SIGKILL
, SIGTSTP
etc.
Ale skąd wiadomo, kto zamówił ten konkretny sygnał, kto wysłał go do określonego procesu i ogólnie, w jaki sposób sygnały wykonują swoje operacje? Jak działają sygnały wewnętrznie?
kill
signals
architecture
Varun Chhangani
źródło
źródło
Odpowiedzi:
Widok 50 000 stóp jest taki:
Sygnał jest generowany albo przez jądro wewnętrznie (na przykład,
SIGSEGV
gdy uzyskiwany jest nieprawidłowy adres lubSIGQUIT
gdy naciśniesz Ctrl+ \), lub przez program korzystający zkill
syscall (lub kilku powiązanych).Jeśli jest to jeden z wywołań systemowych, jądro potwierdza, że proces wywołujący ma wystarczające uprawnienia do wysłania sygnału. Jeśli nie, zwracany jest błąd (i sygnał nie występuje).
Jeśli jest to jeden z dwóch specjalnych sygnałów, jądro bezwarunkowo na niego działa, bez udziału procesu docelowego. Dwa specjalne sygnały to SIGKILL i SIGSTOP. Wszystkie poniższe informacje dotyczące domyślnych działań, sygnałów blokujących itp. Są nieistotne dla tych dwóch.
Następnie jądro sprawdza, co zrobić z sygnałem:
Dla każdego procesu istnieje działanie związane z każdym sygnałem. Istnieje kilka wartości domyślnych, a programy mogą ustawiać różne za pomocą
sigaction
,signal
itp. Należą do nich takie rzeczy jak „całkowicie zignoruj”, „zabij proces”, „zabij proces za pomocą zrzutu pamięci”, „zatrzymaj proces”, itp.Programy mogą również wyłączać dostarczanie sygnałów („blokowane”) na zasadzie sygnał po sygnale. Następnie sygnał pozostaje w oczekiwaniu, aż zostanie odblokowany.
Programy mogą żądać, aby zamiast samego jądra podejmowało jakieś działania, dostarczało sygnał do procesu synchronicznie (z
sigwait
, i in. Lubsignalfd
) lub asynchronicznie (przerywając cokolwiek, co robi proces i wywołując określoną funkcję).Istnieje drugi zestaw sygnałów zwany „sygnałami w czasie rzeczywistym”, które nie mają określonego znaczenia, a także umożliwiają kolejkowanie wielu sygnałów (normalne sygnały ustawiają w kolejce tylko jeden z nich, gdy sygnał jest blokowany). Są one używane w programach wielowątkowych, aby wątki mogły się ze sobą komunikować. Niektóre z nich są używane na przykład w implementacji wątków POSIX glibc. Można ich również używać do komunikacji między różnymi procesami (na przykład można użyć kilku sygnałów w czasie rzeczywistym, aby program fooctl wysłał wiadomość do demona foo).
Aby uzyskać widok nie większy niż 50 000 stóp, spróbuj
man 7 signal
także dokumentację wewnętrzną jądra (lub źródło).źródło
Implementacja sygnału jest bardzo złożona i jest specyficzna dla jądra. Innymi słowy, różne jądra będą implementowały sygnały w różny sposób. Uproszczone wyjaśnienie jest następujące:
CPU, oparty na specjalnej wartości rejestru, ma adres w pamięci, w którym spodziewa się znaleźć „tablicę deskryptorów przerwań”, która w rzeczywistości jest tablicą wektorową. Istnieje jeden wektor dla każdego możliwego wyjątku, np. Dzielenie przez zero lub pułapka, jak INT 3 (debugowanie). Gdy CPU napotka wyjątek, zapisuje flagi i bieżący wskaźnik instrukcji na stosie, a następnie przeskakuje pod adres określony przez odpowiedni wektor. W Linuksie ten wektor zawsze wskazuje na jądro, gdzie znajduje się moduł obsługi wyjątków. Procesor jest już gotowy i jądro Linuksa przejmuje kontrolę.
Pamiętaj, że możesz również uruchomić wyjątek od oprogramowania. Na przykład użytkownik naciska CTRL- C, a następnie to wywołanie trafia do jądra, które wywołuje własny moduł obsługi wyjątków. Ogólnie rzecz biorąc, istnieją różne sposoby dotarcia do modułu obsługi, ale niezależnie od tego, co dzieje się w tej samej podstawowej sytuacji: kontekst zostaje zapisany na stosie i następuje przejście do modułu obsługi wyjątków jądra.
Program obsługi wyjątków decyduje następnie, który wątek powinien otrzymać sygnał. Jeśli wystąpiło coś takiego jak dzielenie przez zero, to jest łatwe: wątek, który spowodował wyjątek, otrzymuje sygnał, ale w przypadku innych rodzajów sygnałów decyzja może być bardzo złożona, aw niektórych nietypowych przypadkach bardziej lub mniej losowy wątek może dostać sygnał.
Aby wysłać sygnał, to co robi jądro, najpierw ustaw wartość wskazującą typ sygnału
SIGHUP
lub cokolwiek innego. To tylko liczba całkowita. Każdy proces ma obszar pamięci „sygnał oczekujący”, w którym zapisywana jest ta wartość. Następnie jądro tworzy strukturę danych z informacjami o sygnale. Ta struktura zawiera sygnał „dyspozycji”, który może być domyślny, ignorować lub obsługiwać. Jądro wywołuje następnie własną funkcjędo_signal()
. Rozpoczyna się kolejna faza.do_signal()
pierwszy decyduje, czy to będzie obsługiwać sygnał. Na przykład, jeśli jest to zabójstwo , todo_signal()
po prostu zabija proces, koniec historii. W przeciwnym razie wygląda na usposobienie. Jeśli ustawienie jest domyślne, wówczasdo_signal()
obsługuje sygnał zgodnie z domyślną polityką zależną od sygnału. Jeśli dyspozycji jest uchwyt, oznacza to, że w programie użytkownika jest funkcja zaprojektowana do obsługi danego sygnału, a wskaźnik do tej funkcji znajdzie się we wspomnianej strukturze danych. W tym przypadku do_signal () wywołuje inną funkcję jądra,handle_signal()
, który następnie przechodzi przez proces powrotu do trybu użytkownika i wywołania tej funkcji. Szczegóły tego przekazania są niezwykle złożone. Ten kod w twoim programie jest zwykle automatycznie łączony z twoim programem, gdy używasz funkcji wsignal.h
.Poprzez odpowiednie sprawdzenie oczekującej wartości sygnału, jądro może ustalić, czy proces obsługuje wszystkie sygnały, a jeśli nie, podejmie odpowiednie działania, które mogą uśpić proces lub go zabić, lub inne działanie, w zależności od sygnału.
źródło
Chociaż na to pytanie udzielono odpowiedzi, pozwól mi opublikować szczegółowy przepływ zdarzeń w jądrze Linuksa.
Jest to w całości skopiowane z postów w systemie Linux: Sygnały z systemu Linux - elementy wewnętrzne na blogu „Linux posts” pod adresem sklinuxblog.blogspot.in.
Program Space User C. Program
Zacznijmy od napisania prostego programu C przestrzeni użytkownika sygnału:
Ten kod przypisuje nową procedurę obsługi sygnału SIGINT. SIGINT można wysłać do uruchomionego procesu za pomocą kombinacji klawiszy Ctrl+ C. Po naciśnięciu przycisku Ctrl+ Cdo zadania wysyłany jest sygnał asynchroniczny SIGINT. Jest to również równoważne z wysłaniem
kill -INT <pid>
polecenia do innego terminala.Jeśli zrobisz
kill -l
(to mała literaL
, co oznacza „listę”), poznasz różne sygnały, które można wysłać do uruchomionego procesu.Do wysyłania określonych sygnałów można także użyć następującej kombinacji klawiszy:
Jeśli skompilujesz i uruchomisz powyższy program C, otrzymasz następujące dane wyjściowe:
Nawet z Ctrl+ Club
kill -2 <pid>
proces się nie zakończy. Zamiast tego wykona procedurę obsługi sygnału i powróci.Jak sygnał jest wysyłany do procesu
Jeśli zobaczymy wewnętrzne sygnały wysyłane do procesu i ustawimy Jprobe z funkcją dump_stack na
__send_signal
funkcję, zobaczymy następujący ślad wywołania:Tak więc główna funkcja wymaga wysłania sygnału:
Teraz wszystko jest skonfigurowane i niezbędne zmiany
task_struct
w procesie.Obsługa sygnału
Sygnał jest sprawdzany / obsługiwany przez proces, gdy powraca z wywołania systemowego lub gdy nastąpi powrót z przerwania. Powrót z wywołania systemowego jest obecny w pliku
entry_64.S
.Wywoływana jest funkcja int_signal, z
entry_64.S
której wywołuje się funkcjędo_notify_resume()
.Sprawdźmy funkcję
do_notify_resume()
. Ta funkcja sprawdza, czy mamyTIF_SIGPENDING
ustawioną flagę wtask_struct
:Połączenia i sygnały systemowe
„Powolne” wywołania systemowe, np. Blokujące odczyt / zapis, wprowadzające procesy w stan oczekiwania:
TASK_INTERRUPTIBLE
lubTASK_UNINTERRUPTIBLE
.Zadanie w stanie
TASK_INTERRUPTIBLE
zostanie zmienione naTASK_RUNNING
stan za pomocą sygnału.TASK_RUNNING
oznacza, że można zaplanować proces.Jeśli zostanie wykonany, jego procedura obsługi sygnału zostanie uruchomiona przed zakończeniem „powolnego” wywołania systemowego.
syscall
Nie zakończy się domyślnie.Jeśli
SA_RESTART
ustawionasyscall
jest flaga, jest restartowana po zakończeniu procedury obsługi sygnału.Referencje
źródło
kill
polecenia, które jest wbudowane w powłokę). (2c) Chociaż średniki po zamknięciu}
funkcji nie są, ściśle mówiąc, błędami, są niepotrzebne i wysoce niekonwencjonalne. (3) Nawet gdyby wszystko było prawidłowe, nie byłaby to bardzo dobra odpowiedź na pytanie. (3a) Pytanie, choć nieco niejasne, wydaje się koncentrować na tym, w jaki sposób aktorzy (użytkownicy i proces) inicjują (tj. Wysyłają ) sygnały. … (Ciąg dalszy)