Jak propagować sygnał do procesów dziecka ze skryptu Bash
- 4306
- 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ł
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 30
Polecenie jest wykonywane w tle; - pid procesu dziecięcego jest „przechowywane” w
Child_pid
zmienny; - Skrypt czeka zakończenie procesu dziecka;
- Skrypt otrzymuje
Sigint
LubSigterm
sygnał -
Czekać
Polecenie powraca natychmiast, bez oczekiwania na rozwiązanie dziecka;
W tym momencie pułapka jest wykonywana. W tym:
- A
Sigterm
sygnał (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