Zabijanie dzieci z rodzicem

Mam program spawnujący i komunikujący się z ciężkimi, niestabilnymi procesami CPU, Nie stworzonymi przeze mnie. Jeśli moja aplikacja ulegnie awarii lub zostanie zabita przez SIGKILL, chcę, aby podprocesy również zostały zabite, więc użytkownik nie musi ich śledzić i zabijać ręcznie.

Wiem, że ten temat był już poruszany, ale wypróbowałem wszystkie opisane metody i żadna z nich nie zdaje się przetrwać testu.

Wiem, że to musi być możliwe, ponieważ terminale robią to cały czas. If I run coś w terminalu, i zabić terminalu, rzeczy zawsze umiera.

Próbowałem atexit, double fork i ptys. atexit nie działa dla sigkill; double fork nie działa w ogóle; i ptys nie znalazłem sposobu na pracę z Pythonem.

Dzisiaj dowiedziałam się o prctl(PR_SET_PDEATHSIG, SIGKILL), który powinien być sposobem na to, by procesy dziecka zlecały zabicie siebie, gdy umiera ich rodzic. Próbowałem go użyć z popen, ale szwy nie mają żadnego efektu: {]}

import ctypes, subprocess
libc = ctypes.CDLL('/lib/libc.so.6')
PR_SET_PDEATHSIG = 1; TERM = 15
implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM)
subprocess.Popen(['gnuchess'], preexec_fn=implant_bomb)

W powyższym dziecko jest tworzone, a rodzic wychodzi. Teraz Można oczekiwać, że gnuchess otrzyma SIGKILL i umrze, ale tak nie jest. nadal mogę go znaleźć w moim Menedżerze procesów przy użyciu 100% CPU.

Czy ktoś może mi powiedzieć, czy coś jest nie tak z moim użyciem prctl?, a może wiesz, jak terminale potrafią zabijać swoje dzieci?

Author: Thomas Ahle, 2009-12-11

7 answers

Prctl 's PR_SET_DEATHSIG może być ustawione tylko dla tego samego procesu, który wywołuje prctl - nie dla żadnego innego procesu, włączając w to jego dzieci. Sposób, w jaki strona podręcznika, na którą wskazuję, wyraża to: "ta wartość jest usuwana na widelcu()" -- fork, Oczywiście, jest to sposób, w jaki inne procesy są wywoływane(w Linuksie i każdym innym uniksowym systemie operacyjnym).

Jeśli nie masz kontroli nad kodem, który chcesz uruchomić w podprocesach (jak by to było w zasadzie dla twojego gnuchess przykład), proponuję najpierw stworzyć osobny mały proces "monitor" z rolą śledzenia wszystkich jego rodzeństwa (twój proces rodzica może poinformować monitor o pid rodzeństwa, ponieważ je rodzi) i wysyłać im zabójcze sygnały, gdy wspólny rodzic umiera (monitor musi to sprawdzić, budząc się co N sekund dla jakiegoś N z wyboru, aby sprawdzić, czy rodzic nadal żyje; użyj select, aby poczekać na więcej informacji od rodzica z limitem czasu N sekund, w ciągu kilku sekund. loop).

Nie jest trywialne, ale takie zadania systemowe często nie są. terminale robią to inaczej (poprzez koncepcję "terminala kontrolującego" dla grupy procesów), ale oczywiście jest trywialne dla każdego dziecka, aby to zablokować(podwójne widelce, nohup itd.).

 6
Author: Alex Martelli,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-11 00:16:41

Wiem, że minęły lata, ale znalazłem proste (nieco chwiejne) rozwiązanie tego problemu. Z procesu macierzystego, owijanie wszystkich wywołań wokół bardzo prostego programu C, który wywołuje prctl (), a następnie exec () rozwiązuje ten problem na Linuksie. Nazywam to "yeshup":

#include <linux/prctl.h>
#include <signal.h>
#include <unistd.h>

int main(int argc, char **argv) {
     if(argc < 2)
          return 1;
     prctl(PR_SET_PDEATHSIG, SIGHUP, 0, 0, 0);
     return execvp(argv[1], &argv[1]);
}

Podczas spawania procesów potomnych z Pythona (lub dowolnego innego języka), możesz uruchomić "yeshup gnuchess [argments]."Przekonasz się, że gdy proces rodzica zostanie zabity, wszystkie procesy dziecka (powinny) otrzymać SIGHUP ładnie.

To działa, ponieważ Linux honoruje wywołanie prctl (nie wyczyści go) nawet po wywołaniu execvp (co efektywnie "przekształca" proces yeshup w proces gnuchess, lub jakiekolwiek polecenie, które tam podasz), w przeciwieństwie do fork().

 12
Author: coolbho3k,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-07-10 23:11:10

Faktycznie odkryłem, że Twoje oryginalne podejście działało dla mnie dobrze - oto dokładny przykładowy kod, który przetestowałem:

Echoer.py

#!/bin/env python

import time
import sys
i = 0
try:
    while True:
        i += 1
        print i
        time.sleep(1)
except KeyboardInterrupt:
    print "\nechoer caught KeyboardInterrupt"
    exit(0)

ParentProc.py

#!/bin/env python

import ctypes
import subprocess
import time

libc = ctypes.CDLL('/lib64/libc.so.6')
PR_SET_PDEATHSIG = 1
SIGINT = 2
SIGTERM = 15

def set_death_signal(signal):
    libc.prctl(PR_SET_PDEATHSIG, signal)

def set_death_signal_int():
    set_death_signal(SIGINT)

def set_death_signal_term():
    set_death_signal(SIGTERM)

#subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_term)
subprocess.Popen(['./echoer.py'], preexec_fn=set_death_signal_int)
time.sleep(1.5)
print "parentProc exiting..."
 4
Author: Paul Molodowitch,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-07-11 18:05:43

Myślałem, że podwójny widelec ma odłączyć się od terminala sterującego. Nie wiem, jak próbujesz go wykorzystać.

To hack, ale zawsze możesz zadzwonić "ps" i wyszukać nazwę procesu, który próbujesz zabić.

 1
Author: BillMan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-11 00:02:36

Widziałem bardzo paskudne sposoby "sprzątania" za pomocą rzeczy takich jak ps xuawww | grep myApp | awk '{ print $1}' | xargs -n1 kill -9

Proces klienta, jeśli zostanie popened, może złapać SIG_PIPE i umrzeć. Istnieje wiele sposobów, aby to zrobić, ale to naprawdę zależy od wielu czynników. Jeśli wrzucisz jakiś kod ping (ping do rodzica) do potomka, możesz upewnić się, że SIG_PIPE zostanie wydane po śmierci. Jeśli to złapie, a powinno, to się skończy. Potrzebujesz dwukierunkowej komunikacji, żeby to działało poprawnie... lub zawsze blokować przeciwko Klientowi jako pomysłodawca komunikacji. Jeśli nie chcesz modyfikować dziecka, zignoruj to.

Zakładając, że nie oczekujesz, że rzeczywisty interpreter Pythona będzie segfault, możesz dodać każdy PID do sekwencji, a następnie zabić przy wyjściu. Powinno to być bezpieczne dla wyjścia, a nawet nieprzewidzianych WYJĄTKÓW. Python ma możliwości wykonywania kodu wyjścia... do sprzątania.

Oto bezpieczniejsze nasty: Dołącz każdy PID potomny do pliku, w tym proces główny (osobny plik). Użyj blokowania plików. Build a Demon watchdog, który patrzy na stan flock() Twojego głównego pid. Jeśli nie jest zablokowany, zabij wszystkie PID na liście PID dziecka. Uruchom ten sam kod przy starcie.

Bardziej paskudne: Zapisz PID do plików, jak powyżej, a następnie wywołaj aplikację w pod-powłoce: (./myMaster; ./killMyChildren)

 1
Author: pestilence669,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-12-11 23:52:46

Zastanawiam się, czy znacznik PR_SET_PDEATHSIG jest czyszczony, mimo że ustawiasz go po fork (i przed exec), więc wydaje się, że z dokumentów jak nie powinien być czyszczony.

Aby przetestować tę teorię, możesz spróbować: użyj tego samego kodu do uruchomienia podprocesu, który jest napisany w C i w zasadzie po prostu wywołuje prctl(PR_GET_PDEATHSIG, &result) i wypisuje wynik.

Kolejna rzecz, którą możesz spróbować: dodanie jawnych zer dla arg3, arg4 i arg5 po wywołaniu prctl. I. E.:

>>> implant_bomb = lambda: libc.prctl(PR_SET_PDEATHSIG, TERM, 0, 0, 0)
 1
Author: Edward Loper,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-10-04 12:06:11

Jest pewne ograniczenie bezpieczeństwa, które należy wziąć pod uwagę, ponieważ jeśli wywołamy setuid po execv, dziecko nie może odebrać sygnału. Pełna lista tych ograniczeń znajduje się tutaj

Powodzenia !
/ Mohamed

 1
Author: Mohamed Hamzaoui,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-06-03 16:11:13