2011-12-30 14 views
9

Pracuję nad kodem serwera, który używa fork() i exec do tworzenia procesów potomnych. Identyfikator PID dziecka jest rejestrowany, gdy fork() powiedzie się i zostanie wyczyszczony, gdy zostanie przechwycony sygnał CHILD.Czy sygnał ZABISKU natychmiast opuszcza proces?

Jeśli serwer musi się zatrzymać, wszystkie programy zostają zabite, ewentualnie z sygnałem KILL. Teraz działa to poprzez iterowanie wszystkich zarejestrowanych PID i czekanie, aż funkcja obsługi sygnału CHILD usunie PID. To się nie powiedzie, jeśli program potomny nie zakończył poprawnie działania. Dlatego chcę użyć kill w połączeniu z waitpid, aby upewnić się, że lista PID jest wyczyszczona i zaloguj się, i wykonaj inne czynności w przeciwnym razie.

Rozważmy kolejny przykładowy kod:

kill(pid, SIGKILL); 
waitpid(pid, NULL, WNOHANG); 

Wyciąg z waitpid(2):

waitpid(): w przypadku sukcesu zwraca identyfikator procesu dziecka, którego stan uległ zmianie; jeśli podano WNOHANG i jeden lub więcej potomków określony przez pid istnieje, ale jeszcze nie zmieniono stanu, to zwracane jest 0. W przypadku błędu zwracane jest -1.

Czy proces wydany przez pid zawsze zniknął, zanim uruchomi się następna funkcja? Czy waitpid zawsze zwróci -1 w powyższym przypadku?

+2

Sygnał KILL jest raczej brutalny, dlaczego nie INT lub HUP? A także, jaki jest kod powrotu 'kill' syscall? – fge

+0

"czekanie na obsługę sygnału DZIECKO do ..." - Nie ma obsługi sygnału dla SIGKILL, wiesz? Wszystkie sygnały, z wyjątkiem SIGSTOP i SIGKILL, wykonają to, co zostało ustawione przez 'sigaction' (na przykład wywołaj procedurę obsługi) lub domyślną. SIGSTOP po prostu się zatrzymuje, a SIGKILL po prostu zabija ten proces. Zawsze. Nie ma obsługi ani warunkowego. (Wyjątkiem jest to, że 'kill' syscall nie działa, ponieważ nie masz wystarczających praw.) – Damon

+0

@Damon:' SIGKILL' nie może być zignorowany przez proces docelowy, to prawda. Ale to nie jest pytanie/problem.Wywołanie systemowe 'kill (2)' w procesie źródłowym może powrócić zanim sygnał zostanie oceniony (przez jądro) w kontekście procesu docelowego. 'kill (2)' jest po prostu bardzo prostą asynchroniczną komunikacją i musi być traktowane jako takie ze wszystkimi implikacjami. –

Odpowiedz

8

Czy proces wydany przez pid zawsze zniknął przed rozpoczęciem następnej funkcji?

Nie ma żadnej gwarancji na to. Na procesorze wieloprocesorowym twój proces może być na procesorze 0, podczas gdy oczyszczanie jądra dla zabitego procesu odbywa się na procesorze 1. Jest to klasyczny wyścig. Nawet na procesorach singlowych nie ma na to żadnej gwarancji.

Czy waitpid zawsze zwróci -1 w powyższym przypadku?

Ponieważ jest to stan wyścigu - w większości przypadków może tak będzie. Ale nie ma żadnej gwarancji.


Ponieważ nie jesteś zainteresowany w stanie to semicode może być bardziej odpowiedni w danym przypadku:

// kill all childs 
foreach(pid from pidlist) 
    kill(pid, SIGKILL); 

// gather results - remove zombies 
while(not_empty(pidlist)) 
    pid = waitpid(-1, NULL, WNOHANG); 
    if(pid > 0) 
     remove_list_item(pidlist, pid); 
    else if(pid == 0) 
     sleep(1); 
    else 
     break; 
+0

Dzięki za wyjaśnienie, nie będę mógł skorzystać z sugerowanego kodu, ponieważ program obsługi SIGCHILD usuwa elementy z listy pidli. To pozostawia mi do wyboru: sprawdzić, czy proces jest uruchomiony, usunąć, jeśli nie, zabić w inny sposób. – Lekensteyn

+0

Po prostu usuń flagę 'WNOHANG', a Twój kod będzie działać zgodnie z oczekiwaniami. –

+0

@Lekensteyn: Jeśli istnieje już program obsługi SIGCHLD, który aktualizuje listę, to ten program obsługi może wywoływać 'waitpid', aby usunąć proces zombie. Następnie pętla 'while' musi tylko czekać na a) wszystkie procesy są martwe i zakopane lub b) jakiś czas oczekiwania na bezpieczeństwo. –

4

Obsługa sygnału KILL będzie działać podczas zabity czasie procesy CPU. Jest to potencjalnie znacznie późniejsze niż twoje połączenie waitpid, szczególnie w obciążonym systemie, dzięki czemu waitpid może bardzo dobrze zwrócić 0.