Przetwarzaj potomków

20

Próbuję zbudować kontener procesu. Kontener uruchomi inne programy. Na przykład - skrypt bash, który uruchamia uruchamianie zadań w tle z użyciem „&”.

Ważna cecha, o którą mi chodzi, to: kiedy zabiję pojemnik, wszystko, co się pod nim pojawiło, powinno zostać zabite. Nie tylko bezpośrednie dzieci, ale także ich potomkowie.

Kiedy rozpocząłem ten projekt, błędnie uwierzyłem, że kiedy zabiłeś proces, jego dzieci również zostały automatycznie zabite. Szukałem porady od osób, które miały ten sam błędny pomysł. Chociaż można złapać sygnał i przekazać dzieciom zabójstwo, nie tego tu szukam.

Wierzę w to, co chcę osiągnąć, ponieważ kiedy zamkniesz Xterm, wszystko, co w nim działało, zostanie zabite, chyba że było to niemożliwe. Obejmuje to procesy osierocone. Właśnie to chcę odtworzyć.

Mam pomysł, że to, czego szukam, dotyczy sesji unixowych.

Jeśli istniałby niezawodny sposób na identyfikację wszystkich potomków procesu, przydatne byłoby również wysyłanie im dowolnych sygnałów. np. SIGUSR1.

Craig Turner
źródło
Cóż, zabicie procesu nadrzędnego powoduje SIGHUPbezpośrednie procesy potomne. Domyślny moduł obsługi sygnału rozłączenia przerywa wykonywanie procesu, więc domyślną trasą jest zabicie wszystkich potomków. Przeczytaj więcej o procesach i grupach procesów.
alex
Zamknięcie Xtermu zabija wszystko odradzające się w Xtermie, ponieważ TTY jest zniszczony. Jeśli możesz wymyślić sposób na utworzenie tty, z którego mogą korzystać procesy potomne, możesz następnie zniszczyć TTY i osiągnąć to samo. Każdy proces, który nie zamknął tego TTY (nohup i przyjaciele), dostanie SIGHUP.
Patrick

Odpowiedzi:

23

Jeśli wyślesz sygnał do procesu, proces ten zostanie zabity. Zastanawiam się, jak zaczęła się plotka, że ​​zabicie procesu zabija inne procesy, wydaje się to szczególnie sprzeczne z intuicją.

Istnieją jednak sposoby na zabicie więcej niż jednego procesu. Ale nie wyślesz sygnału do jednego procesu. Możesz zabić całą grupę procesów , wysyłając sygnał na numer -1234, gdzie 1234 to PGID (identyfikator grupy procesów), który jest PID lidera grupy procesów. Po uruchomieniu potoku cały potok zaczyna się jako grupa procesów (aplikacje mogą to zmienić przez wywołanie setpgidlub setpgrp).

Kiedy uruchamiasz procesy w tle ( foo &), są one we własnej grupie procesów. Grupy procesów służą do zarządzania dostępem do terminala; zwykle tylko grupa procesów pierwszego planu ma dostęp do terminala. Zadania w tle pozostają w tej samej sesji , ale nie ma możliwości zabicia całej sesji, ani nawet wyliczenia grup procesów lub procesów w sesji, więc to niewiele pomaga.

Po zamknięciu terminala jądro wysyła sygnał SIGHUPdo wszystkich procesów, które mają go jako terminal kontrolny . Te procesy tworzą sesję , ale nie wszystkie sesje mają terminal kontrolny. W przypadku twojego projektu jedną z możliwości jest zatem uruchomienie wszystkich procesów we własnym terminalu, utworzonym przez skrypt , screen itp. Zabij proces emulatora terminala, aby zabić zawarte w nim procesy (zakładając, że się nie rozdzieliły setsid).

Możesz zapewnić większą izolację, uruchamiając procesy jako ich własny użytkownik, który nie robi nic innego. Następnie łatwo jest zabić wszystkie procesy: uruchom kill( wywołanie systemowe lub narzędzie ) jako ten użytkownik i użyj -1 jako argumentu PID, aby zabić, co oznacza „wszystkie procesy tego użytkownika”.

Możesz zapewnić jeszcze większą izolację, ale przy znacznie większej konfiguracji, uruchamiając zawarte procesy w rzeczywistym kontenerze .

Gilles „SO- przestań być zły”
źródło
Jeśli programujesz własne procesy, możesz użyć czegoś takiego jak :,prctl(PR_SET_PDEATHSIG, SIGHUP); więcej informacji: man7.org/linux/man-pages/man2/prctl.2.html
Alexis Wilke
Gilles, wspominasz o tym - „Kiedy zamykasz terminal, jądro wysyła sygnał SIGHUP do wszystkich procesów, które mają go jako terminal kontrolny”. Z tego, co sądzę, terminal wysyła SIGHUP do lidera sesji (powłoki), który przesyła go do wszystkich grup procesów w sesji. Który z nich byłby odpowiedni?
iruvar
4

W skrypcie nadrzędnym przechwytuj sygnał zabicia i każ mu zabić wszystkie dzieci. Na przykład,

#!/bin/bash
# kill the parent and children together
trap "kill 0" EXIT
# create all the children
for n in $(seq 1 100)
do
    ( echo "begin $n"; sleep 60; echo "end $n" ) &
done
# wait for the children to complete
wait
Andrew Gilmartin
źródło
2

Pewnym sposobem na identyfikację wszystkich potomków procesu jest użycie polecenia, w pstree <pid>którym pid jest identyfikatorem procesu nadrzędnego.

Przeczytaj stronę podręcznika pstree tutaj .

Aby zasygnalizować wszystkim członkom grupy procesów: killpg(<pgrp>, <sig>);
gdzie pgrp jest numerem grupy procesów, a sig jest sygnałem.

Aby czekać na dzieci w określonej grupie procesów: waitpid(-<pgrp>, &status, ...);

Alternatywą dla tego, co robisz, jest uruchomienie kontenera procesów w nowej powłoce bash. Utwórz nową powłokę bash za pomocą polecenia, basha następnie uruchom swoje procesy. Gdy chcesz, aby wszystkie procesy zostały zakończone, wyjdź z powłoki za pomocą polecenia exit.

Chris Ting
źródło
Myślę, że killpg pozwoli mi robić to, co muszę. Dzięki.
Craig Turner
1

Posługiwać się

unshare -fp --kill-child -- yourprogram

Jeśli zabijesz unshare, wszystkie procesy potomne (które yourprogrammogły się odrodzić) zostaną zabite.

Jest to teraz możliwe przy użyciu util-linux 2.32; Zaimplementowałem to wcześniej . Wymaga to przestrzeni nazw użytkownika (opcja konfiguracji jądra CONFIG_USER_NS=y) lub uprawnień roota. Zobacz także tutaj .

nh2
źródło
0

Komenda rkill z pakietu pslist wysyła podany sygnał (lub SIGTERMdomyślnie) do określonego procesu i wszystkich jego potomków:

rkill [-SIG] pid/name...
Onlyjob
źródło
0

Inna opcja zabicia wszystkich potomków powłoki: jobs -p | xargs -n 1 pkill -P

Doug Fawley
źródło