Jak propagować sygnał do procesów dziecka ze skryptu Bash
- 4343
- 339
- Maria Piwowarczyk
Załóżmy, że piszemy skrypt, który spawnuje jeden lub więcej długich procesów; Jeśli wspomniany skrypt otrzyma sygnał taki jak Sigint Lub Sigterm, Prawdopodobnie chcemy, aby jego dzieci również zostały zakończone (zwykle, gdy rodzic umiera, dzieci przetrwają). Możemy również chcieć wykonać zadania oczyszczające, zanim sam skrypt wyjdzie. Aby móc osiągnąć nasz cel, musimy najpierw dowiedzieć się o grupach procesowych i jak wykonać proces w tle.
W tym samouczku się nauczysz:
- Co to jest grupa procesowa
- Różnica między procesami pierwszego planu i tła
- Jak wykonać program w tle
- Jak używać skorupy
Czekaćwbudowane, aby czekać na proces wykonany w tle - Jak zakończyć procesy dziecka, gdy rodzic otrzymuje sygnał
Jak propagować sygnał do procesów dziecka ze skryptu Bash Zastosowane wymagania i konwencje oprogramowania
| Kategoria | Wymagania, konwencje lub wersja oprogramowania |
|---|---|
| System | Niezależny dystrybucja |
| Oprogramowanie | Brak konkretnego oprogramowania |
| Inny | Nic |
| Konwencje | # - Wymaga, aby podane polecenia Linux są wykonywane z uprawnieniami root bezpośrednio jako użytkownik root lub za pomocą sudo Komenda$ - Wymaga, aby podane polecenia Linux zostały wykonane jako zwykły użytkownik niepewny |
Prosty przykład
Utwórzmy bardzo prosty skrypt i symuluj uruchomienie długiego procesu:
#!/BIN/BASH TRAP „Otrzymano sygnał echo!„Sigint Echo” Skrypt PID to $ „Sleep 30 Kopiuj Pierwszą rzeczą, którą zrobiliśmy w scenariuszu, było stworzenie pułapki do złapania Sigint i wydrukuj wiadomość po odbieraniu sygnału. Wydrukowaliśmy nasz skrypt pid: Możemy zdobyć rozszerzenie $$ zmienny. Następnie wykonaliśmy spać polecenie symulacji długotrwałego procesu (30 sekundy).
Zapisujemy kod w pliku (powiedzmy, że się nazywa test.cii), uczyń go wykonywaniem i uruchom go z emulatora terminalu. Otrzymujemy następujący wynik:
Skrypt PID to 101248
Jeśli koncentrujemy się na emulatorze terminalu i naciśnij Ctrl+C podczas uruchomienia skryptu, a Sigint sygnał jest wysyłany i obsługiwany przez nasz pułapka:
Skrypt PID jest 101248 ^Csignal otrzymany!
Chociaż pułapka obsługiwała sygnał zgodnie z oczekiwaniami, skrypt i tak został przerwany. Dlaczego tak się stało? Ponadto, jeśli wyślemy Sigint sygnał do skryptu za pomocą zabić Polecenie, wynik, który otrzymujemy, jest zupełnie inny: pułapka nie jest natychmiast wykonywana, a skrypt trwa, dopóki proces dziecięcy nie wyjdzie (po 30 sekundy „spania”). Dlaczego ta różnica? Zobaczmy…
Grupy procesowe, na pierwszym planie i zadania w tle
Zanim odpowiemy na powyższe pytania, musimy lepiej zrozumieć koncepcję grupa procesowa.
Grupa procesowa to grupa procesów, które dzielą to samo PGID (identyfikator grupy procesowej). Kiedy członek grupy procesowej tworzy proces potomny, proces ten staje się członkiem tej samej grupy procesowej. Każda grupa procesowa ma lidera; Możemy go łatwo rozpoznać, ponieważ to pid i PGID są takie same.
Możemy wizualizować pid I PGID uruchamiania procesów za pomocą Ps Komenda. Wyjście polecenia można było dostosować, aby wyświetlano tylko pola, które jesteśmy zainteresowani: w tym przypadku CMD, Pid I PGID. Robimy to za pomocą -o Opcja, zapewnianie odcinanej przecinki listy pól jako argumentu:
$ ps -a -o pid, pGID, cmd
Jeśli uruchomimy polecenie, gdy nasz skrypt uruchamia odpowiednią część danych wyjściowych, jest następująca:
PID PGID CMD 298349 298349 /BIN /BASH ./test.SH 298350 298349 Sleep 30
Możemy wyraźnie zobaczyć dwa procesy: pid pierwszego jest 298349, Tak samo jak to PGID: To jest lider grupy procesów. Został stworzony, kiedy uruchomiliśmy skrypt, jak widać w CMD kolumna.
Ten główny proces uruchomił proces dziecka z poleceniem Sleep 30: Zgodnie z oczekiwaniami dwa procesy są w tej samej grupie procesów.
Kiedy naciskaliśmy CTRL-C, skupiając się na terminalu, z którego uruchomiono skrypt, sygnał nie został wysłany tylko do procesu nadrzędnego, ale do całej grupy procesów. Która grupa procesowa? Grupa procesów na pierwszym planie terminala. Wszystkie procesy członek tej grupy są nazywane Procesy na pierwszym planie, Wszystkie pozostałe są nazywane Procesy w tle. Oto, co ma do powiedzenia podręcznik Bash w tej sprawie:
CZY WIEDZIAŁEŚ?Aby ułatwić wdrożenie interfejsu użytkownika do kontroli zadań, system operacyjny utrzymuje pojęcie bieżącego identyfikatora grupy procesów terminali. Członkowie tej grupy procesowej (procesy, których identyfikator grupy procesu jest równy bieżącemu identyfikatorowi grupy procesowej), odbierają sygnały generowane klawiaturą, takie jak Sigint. Mówi się, że te procesy są na pierwszym planie. Procesy tła to te, których identyfikator grupy procesu różni się od terminala; Takie procesy są odporne na sygnały generowane przez klawiaturę.Kiedy wysłaliśmy Sigint sygnał z zabić Zamiast tego polecenie celowaliśmy tylko w PID procesu nadrzędnego; Bash wykazuje określone zachowanie, gdy sygnał jest odbierany, gdy czeka na zakończenie programu: „Kod pułapki” dla tego sygnału nie jest wykonywana, dopóki proces ten. Dlatego komunikat „otrzymany sygnał” został wyświetlony dopiero po spać Komenda wyszła.
Aby powtórzyć to, co dzieje się, gdy naciśniemy CTRL-C w terminalu za pomocą zabić polecenie, aby wysłać sygnał, musimy kierować się do grupy procesowej. Możemy wysłać sygnał do grupy procesowej za pomocą negacja PID lidera procesu, Załóżmy więc pid lidera procesu jest 298349 (Jak w poprzednim przykładzie), uruchomimy:
$ Kill -2 -298349
Zarządzaj propagacją sygnału od wnętrza skryptu
Załóżmy teraz, że uruchamiamy długotrwały skrypt z nie interaktywnej powłoki i chcemy, aby wspomniany skrypt zarządzał propagacją sygnału automatycznie, aby po otrzymaniu sygnału takiego jak sygnał Sigint Lub Sigterm kończy swoje potencjalnie długotrwałe dziecko, ostatecznie wykonując zadania oczyszczające przed wyjściem. Jak możemy to zrobić?
Podobnie jak wcześniej, możemy poradzić sobie z sytuacją, w której sygnał jest odbierany w pułapce; Jednak, jak widzieliśmy, jeśli sygnał zostanie odebrany, podczas gdy powłoka czekała na zakończenie programu, „kod pułapki” jest wykonywany dopiero po wyjściu z procesu dziecka.
Nie tego chcemy: chcemy, aby kod pułapki został przetworzony, gdy tylko proces nadrzędny odbiera sygnał. Aby osiągnąć nasz cel, musimy wykonać proces dziecka w tło: Możemy to zrobić, umieszczając I symbol po poleceniu. W naszym przypadku pisalibyśmy:
#!Otrzymany sygnał echa/bin/bash!„Sigint echo„ Skrypt pid to $ ”snu 30 & Kopiuj Gdybyśmy zostawili skrypt w ten sposób, proces nadrzędny wychodziłby zaraz po wykonaniu Sleep 30 polecenie, pozostawiając nas bez szansy na wykonanie zadań oczyszczających po zakończeniu lub przerwaniu. Możemy rozwiązać ten problem, używając powłoki Czekać Wbudowany. Strona pomocy z Czekać definiuje to w ten sposób:
Czeka na każdy proces zidentyfikowany przez identyfikator, który może być identyfikatorem procesu lub specyfikacją zadania, i zgłasza jego status rozwiązania. Jeśli identyfikator nie zostanie podany, czeka na wszystkie obecnie aktywne procesy dzieci, a status powrotu wynosi zero.
Po ustawieniu procesu do wykonania w tle możemy go odzyskać pid w $! zmienny. Możemy to przekazać jako argument Czekać Aby proces dla rodziców poczekaj na jego dziecko:
#!Otrzymany sygnał echa/bin/bash!„Sigint echo„ Script PID to $ ”snu 30 i czekaj $! Kopiuj Skończyliśmy? Nie, nadal występuje problem: odbiór sygnału obsługiwanego w pułapce wewnątrz skryptu powoduje Czekać Zbudowany do natychmiastowego powrotu, nie czekając na zakończenie polecenia w tle. To zachowanie jest udokumentowane w instrukcji BASH:
Aby rozwiązać ten problem, musimy użyć Czekać znowu, być może jako część samej pułapki. Oto, jak w końcu może wyglądać nasz skrypt:
#!/bin/bash CleanUp () echo "czyszczenie…" # nasz kod czyszczenia trafi tutaj Otrzymano sygnał echo pułapki!; Kill "$ child_pid"; czekać „$ child_pid”; Oczyszczanie Sigint Sigterm Echo „Skrypt PID to $„ Sleep 30 & Child_pid = ”$!„Poczekaj” $ child_pid ” Kopiuj W skrypcie stworzyliśmy posprzątać funkcja, w której moglibyśmy wstawić nasz kod czyszczenia i stworzyć nasz pułapka Złap także Sigterm sygnał. Oto, co się dzieje, gdy uruchamiamy ten skrypt i wysyłamy do niego jeden z tych dwóch sygnałów:
- Skrypt jest uruchomiony i
Sleep 30Polecenie jest wykonywane w tle; - pid procesu dziecięcego jest „przechowywane” w
Child_pidzmienny; - Skrypt czeka zakończenie procesu dziecka;
- Skrypt otrzymuje
SigintLubSigtermsygnał -
CzekaćPolecenie powraca natychmiast, bez oczekiwania na rozwiązanie dziecka;
W tym momencie pułapka jest wykonywana. W tym:
- A
Sigtermsygnał (zabićdomyślnie) jest wysyłany doChild_pid; - My
CzekaćAby upewnić się, że dziecko zostało zakończone po otrzymaniu tego sygnału. - Po
Czekaćzwroty, wykonujemyposprzątaćfunkcjonować.
Propaguj sygnał do wielu dzieci
W powyższym przykładzie pracowaliśmy ze skryptem, który miał tylko jeden proces dziecka. Co jeśli scenariusz ma wiele dzieci i co, jeśli niektórzy z nich mają własne dzieci?
W pierwszym przypadku jeden szybki sposób na zdobycie pids spośród wszystkich dzieci jest używanie Jobs -p Polecenie: To polecenie wyświetla PID wszystkich aktywnych zadań w bieżącej powładzie. Możemy niż użyć zabić zakończyć je. Oto przykład:
#!/bin/bash CleanUp () echo "czyszczenie…" # nasz kod czyszczenia trafi tutaj Otrzymano sygnał echo pułapki!; Zabij $ (Jobs -p); Czekać; Oczyszczanie Sigint Sigterm Echo „Skrypt PID to $” Sleep 30 & Sleep 40 i czekaj Kopiuj Skrypt uruchamia dwa procesy w tle: przy użyciu Czekać Wbudowane bez argumentów, czekamy na wszystkie z nich i utrzymujemy proces rodzica przy życiu. Kiedy Sigint Lub Sigterm Sygnały są odbierane przez skrypt, wysyłamy Sigterm dla obu z nich, powrót przez PIDS Jobs -p Komenda (stanowisko sam jest wbudowanym skorupą, więc kiedy go używamy, nie powstaje nowy proces).
Jeśli dzieci mają własny proces dzieci i chcemy je wszystkie zakończyć, gdy przodek otrzyma sygnał, możemy wysłać sygnał do całej grupy procesowej, jak widzieliśmy wcześniej.
To jednak stanowi problem, ponieważ wysyłając sygnał zakończenia do grupy procesowej, wprowadzilibyśmy pętlę „Sygnał-sent/uwięziony sygnał”. Pomyśl o tym: w pułapka Do Sigterm Wysyłamy Sigterm sygnał dla wszystkich członków grupy procesowej; Obejmuje to sam skrypt nadrzędny!
Aby rozwiązać ten problem i nadal być w stanie wykonać funkcję oczyszczania po zakończeniu procesów dziecięcych, musimy zmienić pułapka Do Sigterm Na przykład tuż przed wysłaniem sygnału do grupy procesowej:
#!/bin/bash czyszczenie () echo "czyszczenie…" # nasz kod czyszczenia trafi tutaj pułapka „pułapka” „sigterm; Zabij 0; Czekać; Oczyszczanie Sigint Sigterm Echo „Skrypt PID to $” Sleep 30 & Sleep 40 i czekaj Kopiuj W pułapce przed wysłaniem Sigterm Do grupy procesowej zmieniliśmy Sigterm pułapka, tak że proces nadrzędny ignoruje sygnał i wpływa tylko na jego potomkowie. Zauważ również, że w pułapce, aby zasygnalizować grupę procesową, użyliśmy zabić z 0 jako pid. To jest rodzaj skrótu: kiedy pid Przeszedł do zabić Jest 0, wszystkie procesy w aktualny Grupa procesowa jest sygnalizowana.
Wnioski
W tym samouczku dowiedzieliśmy się o grupach procesowych i jakiej różnicy między procesami na pierwszym planie i. Dowiedzieliśmy się, że Ctrl-C wysyła Sigint sygnał do całej grupy procesowej na pierwszym planie terminalu kontrolującego i nauczyliśmy się wysłać sygnał do grupy procesowej za pomocą zabić. Nauczyliśmy się również, jak wykonywać program w tle i jak korzystać z Czekać Shell wbudowany, aby czekać, aż wyjdzie bez utraty powłoki nadrzędnej. Wreszcie widzieliśmy, jak skonfigurować skrypt, aby po otrzymaniu sygnału zakończyło swoje dzieci przed wyjściem. Przegapiłem coś? Czy masz swoje osobiste przepisy do wykonania zadania? Nie wahaj się daj mi znać!
Powiązane samouczki Linux:
- Bash Tła zarządzanie procesami
- Wielokrotyściowe skrypty i zarządzanie procesami w…
- Wprowadzenie do automatyzacji, narzędzi i technik Linuksa
- Jak uruchomić polecenie w tle na Linux
- Mastering Bash Script Loops
- Jak zainstalować sygnał na Linux
- Jak śledzić połączenia systemowe wykonane przez proces z Strace na…
- Mint 20: Lepsze niż Ubuntu i Microsoft Windows?
- Porównanie Linux Apache Prefork vs Pracowni
- Jak pracować z grupami pakietów DNF