Krótka odpowiedź
W bash
(idash
) różne komunikaty „status zadania” nie są wyświetlane przez programy obsługi sygnałów, ale wymagają wyraźnego sprawdzenia. To sprawdzenie jest wykonywane tylko przed wyświetleniem nowego monitu, prawdopodobnie nie przeszkadzając użytkownikowi podczas pisania nowej komendy.
Komunikat nie jest wyświetlany tuż przed wyświetleniem monitu po, kill
prawdopodobnie dlatego, że proces nie jest jeszcze martwy - jest to szczególnie prawdopodobny warunek, ponieważ kill
jest to wewnętrzne polecenie powłoki, więc jest bardzo szybkie do wykonania i nie wymaga rozwidlenia.
Wykonanie tego samego eksperymentu z killall
, zamiast tego, zwykle powoduje natychmiastowy komunikat „zabity”, znak, że przełączniki czasu / kontekstu / cokolwiek wymaganego do wykonania polecenia zewnętrznego powodują opóźnienie wystarczająco długie, aby proces mógł zostać zabity, zanim formant powróci do powłoki .
matteo@teokubuntu:~$ dash
$ sleep 60 &
$ ps
PID TTY TIME CMD
4540 pts/3 00:00:00 bash
4811 pts/3 00:00:00 sh
4812 pts/3 00:00:00 sleep
4813 pts/3 00:00:00 ps
$ kill -9 4812
$
[1] + Killed sleep 60
$ sleep 60 &
$ killall sleep
[1] + Terminated sleep 60
$
Długa odpowiedź
dash
Przede wszystkim rzuciłem okiem na dash
źródła, ponieważ dash
wykazuje to samo zachowanie, a kod jest z pewnością prostszy niż bash
.
Jak wspomniano powyżej, wydaje się, że chodzi o to, że komunikaty o statusie zadania nie są emitowane z procedury obsługi sygnału (która może zakłócać „normalny” przepływ kontroli powłoki), ale są konsekwencją jawnego sprawdzenia ( showjobs(out2, SHOW_CHANGED)
wywołania dash
), które jest wykonywane tylko przed zażądaniem nowego wejścia od użytkownika, w pętli REPL.
Zatem jeśli powłoka jest zablokowana w oczekiwaniu na dane wejściowe użytkownika, taki komunikat nie jest emitowany.
Dlaczego więc kontrola przeprowadzona tuż po zabiciu nie pokazuje, że proces został faktycznie zakończony? Jak wyjaśniono powyżej, prawdopodobnie dlatego, że jest za szybki. kill
jest wewnętrzną komendą powłoki, więc jest bardzo szybka do wykonania i nie wymaga rozwidlenia, dlatego gdy natychmiast po kill
przeprowadzeniu kontroli proces jest nadal aktywny (lub przynajmniej wciąż jest zabijany).
bash
Zgodnie z oczekiwaniami bash
bycie znacznie bardziej złożoną powłoką było trudniejsze i wymagało trochę gdb
-fu.
Ślad wsteczny po wysłaniu tej wiadomości jest podobny
(gdb) bt
#0 pretty_print_job (job_index=job_index@entry=0, format=format@entry=0, stream=0x7ffff7bd01a0 <_IO_2_1_stderr_>) at jobs.c:1630
#1 0x000000000044030a in notify_of_job_status () at jobs.c:3561
#2 notify_of_job_status () at jobs.c:3461
#3 0x0000000000441e97 in notify_and_cleanup () at jobs.c:2664
#4 0x00000000004205e1 in shell_getc (remove_quoted_newline=1) at /Users/chet/src/bash/src/parse.y:2213
#5 shell_getc (remove_quoted_newline=1) at /Users/chet/src/bash/src/parse.y:2159
#6 0x0000000000423316 in read_token (command=<optimized out>) at /Users/chet/src/bash/src/parse.y:2908
#7 read_token (command=0) at /Users/chet/src/bash/src/parse.y:2859
#8 0x00000000004268e4 in yylex () at /Users/chet/src/bash/src/parse.y:2517
#9 yyparse () at y.tab.c:2014
#10 0x000000000041df6a in parse_command () at eval.c:228
#11 0x000000000041e036 in read_command () at eval.c:272
#12 0x000000000041e27f in reader_loop () at eval.c:137
#13 0x000000000041c6fd in main (argc=1, argv=0x7fffffffdf48, env=0x7fffffffdf58) at shell.c:749
Połączenie sprawdzające martwe zlecenia i spółkę. jest notify_of_job_status
(to mniej więcej odpowiednik showjobs(..., SHOW_CHANGED)
in dash
); # 0- # 1 związane są z jego wewnętrznym działaniem; 6-8 to wygenerowany przez yacc kod parsera; 10-12 to pętla REPL.
Interesującym miejscem jest tutaj # 4, tj. Skąd notify_and_cleanup
pochodzi połączenie. Wygląda na to bash
, że w przeciwieństwie do tego dash
może sprawdzać zakończone zadania przy każdym znaku odczytanym z wiersza poleceń, ale oto, co znalazłem:
/* If the shell is interatctive, but not currently printing a prompt
(interactive_shell && interactive == 0), we don't want to print
notifies or cleanup the jobs -- we want to defer it until we do
print the next prompt. */
if (interactive_shell == 0 || SHOULD_PROMPT())
{
#if defined (JOB_CONTROL)
/* This can cause a problem when reading a command as the result
of a trap, when the trap is called from flush_child. This call
had better not cause jobs to disappear from the job table in
that case, or we will have big trouble. */
notify_and_cleanup ();
#else /* !JOB_CONTROL */
cleanup_dead_jobs ();
#endif /* !JOB_CONTROL */
}
Tak więc w trybie interaktywnym celowe jest opóźnianie sprawdzania do momentu pojawienia się nowego monitu, prawdopodobnie nie zakłócając wprowadzania poleceń przez użytkownika. Jeśli chodzi o to, dlaczego sprawdzanie nie wykrywa martwego procesu podczas wyświetlania nowego monitu bezpośrednio po kill
, poprzednie wytłumaczenie pozostaje w mocy (proces nie jest jeszcze martwy).
pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"
ale mniej się nie pojawi