Czy sygnał można zignorować (utracić)?

9

Mam aplikację, która komunikuje się z pracownikami za pomocą sygnałów (w szczególności SIGUSR1 / SIGUSR2 / SIGSTOP).

Czy mogę ufać, że cokolwiek się stanie, każdy sygnał zostanie dostarczony i przetworzony przez program obsługi?

Co się stanie, jeśli sygnały będą wysyłane szybciej, niż aplikacja nie jest w stanie obsłużyć ich (np. Z powodu dużego obciążenia hosta w tej chwili)?

KaP
źródło

Odpowiedzi:

8

Oprócz problemu „zbyt wielu sygnałów” sygnały można wyraźnie zignorować. Od man 2 signal:

If the signal signum is delivered to the process, then one of the
following happens:    
  *  If the disposition is set to SIG_IGN, then the signal is ignored.

Sygnały można również blokować. Od man 7 signal;

A signal may be blocked, which means that it will not be delivered
until it is later unblocked.  Between the time when it is generated
and when it is delivered a signal is said to be pending.

Zarówno zablokowane, jak i zignorowane zestawy sygnałów są dziedziczone przez procesy potomne, więc może się zdarzyć, że proces nadrzędny aplikacji zignoruje lub zablokuje jeden z tych sygnałów.

Co się stanie, gdy dostarczonych zostanie wiele sygnałów, zanim proces zakończy obsługę poprzednich? To zależy od systemu operacyjnego. signal(2)Podręcznika powiązany powyżej Omawia:

  • System V zresetuje ustawienie sygnału do wartości domyślnej. Co gorsza, szybkie dostarczanie wielu sygnałów spowodowałoby wywołania rekurencyjne (?).
  • BSD automatycznie blokuje sygnał do momentu zakończenia procedury obsługi.
  • W Linuksie zależy to od ustawionych flag kompilacji GNU libc, ale oczekiwałbym zachowania BSD.
muru
źródło
4
Strona signal(2)podręcznika Linuksa dla sugeruje zdecydowanie, aby uniknąć tego zamieszania, używając sigaction(2)zamiast tego.
Nate Eldredge
7

Nie możesz ufać, że każdy wysłany sygnał zostanie dostarczony. Na przykład jądro Linuksa „łączy” SIGCHLD, jeśli proces zajmuje dużo czasu w obsłudze SIGCHLD z zakończonego procesu potomnego.

Aby odpowiedzieć na inną część twojego pytania, sygnały zostają „umieszczone w kolejce” wewnątrz jądra, jeśli liczba różnych sygnałów dotrze w zbyt krótkim odstępie czasu.

Należy użyć sigaction(), aby ustawić obsługi sygnału z sa_sigactionczłonka siginfo_t, ustawienie sa_maskczłonek siginfo_tstarannie argument. Myślę, że oznacza to maskowanie przynajmniej wszystkich sygnałów „asynch”. Według strony podręcznika użytkownika dla Linuksa sigaction()ukryjesz również przetwarzany sygnał. Myślę, że powinieneś ustawić sa_flagsczłonka na SA_SIGINFO, ale nie pamiętam, dlaczego mam takie przesądy. Wierzę, że dzięki temu twój proces będzie obsługiwał sygnał, który pozostanie ustawiony bez warunków wyścigu i taki, który nie zostanie przerwany przez większość innych sygnałów.

Napisz swoją funkcję obsługi sygnału bardzo, bardzo ostrożnie. Zasadniczo wystarczy ustawić zmienną globalną, aby wskazać, że sygnał został złapany, a reszta procesu zajmie się żądaną akcją dla tego sygnału. W ten sposób sygnały będą maskowane przez jak najmniej czasu.

Ponadto bardzo dokładnie przetestujesz kod obsługi sygnału. Przeprowadź go w małym procesie testowym i wyślij jak najwięcej sygnałów SIGUSR1 i SIGUSR2, na przykład z 2 lub 3 programów do wysyłania sygnałów specjalnego przeznaczenia. Miksuj także inne sygnały, gdy masz pewność, że Twój kod może szybko i poprawnie obsługiwać SIGUSR1 i SIGUSR2. Przygotuj się na trudne debugowanie.

Jeśli używasz Linuksa i tylko Linuksa, możesz pomyśleć o signalfd()utworzeniu deskryptora pliku, który możesz select()lub odpytać, aby odebrać te sygnały. Użycie signalfd()może ułatwić debugowanie.

Bruce Ediger
źródło
2
Łączą się nie tylko SIGCLD: wszystkie sygnały są potencjalnie łączone, jeśli zostaną dostarczone, zanim zostaną przetworzone.
Gilles „SO- przestań być zły”
Czy jest jakaś miara tego, jak długo jest „za długi” dla sygnałów SIGCHLD? Obecnie w moim programie występuje takie zachowanie, a mój moduł obsługi sygnału nie zajmuje więcej niż ~ 100 ms.
xrisk
@Rishav - według mojej wiedzy nie ma sposobu, aby dowiedzieć się, co to jest „zbyt długo”. Spodziewałbym się, że ogólne obciążenie systemu jest ważne. Oznacza to, że inne procesy i jądro mają wpływ na „jak długo” między sygnałami, aby mogły się połączyć. Myślę, że nie jest to pomocna odpowiedź.
Bruce Ediger,
6

Gwarantuje się, że sygnał zostanie dostarczony w tym sensie, że jeśli proces zostanie pomyślnie wywołany kill, wtedy cel otrzyma sygnał. Jest to asynchroniczne: nadawca nie może wiedzieć, kiedy sygnał zostanie odebrany lub przetworzony. Nie gwarantuje to jednak, że sygnał zostanie dostarczony. Cel może umrzeć, zanim będzie mógł przetworzyć sygnał. Jeśli cel ignoruje sygnał w momencie jego dostarczenia, sygnał nie będzie miał wpływu. Jeśli cel odbierze wiele wystąpień tego samego numeru sygnału, zanim będzie mógł je przetworzyć, sygnały mogą (i zazwyczaj są) scalone: ​​jeśli wyślesz ten sam sygnał dwukrotnie do procesu, nie będziesz wiedział, czy proces odbierze sygnał raz lub dwa razy. Sygnały są w większości zaprojektowane do zabicia procesu lub jako sposób na zwrócenie uwagi na proces, nie są przeznaczone do komunikacji jako takiej.

Jeśli potrzebujesz niezawodnej dostawy, potrzebujesz innego mechanizmu komunikacji. Istnieją dwa główne mechanizmy komunikacji między procesami: potok umożliwia komunikację jednokierunkową; gniazdo umożliwia dwukierunkową komunikację i wiele połączeń do tego samego serwera. Jeśli potrzebujesz, aby cel przetwarzał tyle powiadomień, ile je wysyłasz, wysyłaj bajty za pomocą potoku.

Gilles „SO- przestań być zły”
źródło
4
Czy miałeś na myśli napisanie „Gwarantowany jest dostarczenie sygnału”, ponieważ opisałeś niektóre sposoby, w których sygnał nie zostałby dostarczony (tj. Proces umarł przed jego otrzymaniem lub sygnały zostały połączone)?
Johnny
2

Jądro może łączyć standardowe sygnały, jeśli więcej niż jeden jest dostarczany, gdy jest zablokowany. Z drugiej strony sygnały w czasie rzeczywistym nie są podobnie upośledzone.

Ze strony podręcznika użytkownika signal (7) :

Sygnały w czasie rzeczywistym wyróżniają się następującymi elementami:

  1. Wiele wystąpień sygnałów w czasie rzeczywistym może być umieszczanych w kolejce. Natomiast jeśli dostarczonych jest wiele wystąpień sygnału standardowego, podczas gdy sygnał ten jest obecnie blokowany, to tylko jedno wystąpienie jest w kolejce.

Spróbuj użyć sygnału z liczbą w zakresie od SIGRTMIN do SIGRTMAX.

Graham Rushton
źródło
Istnieje limit sygnałów w czasie rzeczywistym, ale jest dość wysoki. Sygnał zostanie odrzucony, jeśli liczba zaległych sygnałów oczekujących wysłanych przez użytkownika przekroczy RLIMIT_SIGPENDING. ulimit -ipokazuje tę wartość jako 63432 na Ubuntu 18.04.
bain