Коротка відповідь
У bash
(і dash
) різні повідомлення про "статус завдання" не відображаються від обробників сигналів, але вимагають чіткої перевірки. Ця перевірка виконується лише до того, як буде надано нове запит, імовірно, щоб не заважати користувачеві під час введення нової команди.
Повідомлення не відображається перед тим, як з'явиться запит після kill
відображення, можливо, тому що процес ще не загинув - це особливо вірогідна умова, оскільки kill
це внутрішня команда оболонки, тому виконувати її дуже швидко і не потрібно розгортання.
Зробивши той же експеримент із killall
, натомість, зазвичай негайно видає повідомлення "вбитий", підпишіть, що перемикачі часу / контексту / все, що потрібно для виконання зовнішньої команди, викликають затримку, достатню для затримки процесу, перш ніж управління повернеться до оболонки .
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
$
Довга відповідь
dash
Перш за все, я ознайомився з dash
джерелами, оскільки dash
проявляє таку ж поведінку, і код, безумовно, простіший за bash
.
Як було сказано вище, суть полягає в тому, що повідомлення про стан завдання не випромінюються від обробника сигналу (що може перервати "нормальний" потік управління оболонкою), але вони є наслідком явної перевірки ( showjobs(out2, SHOW_CHANGED)
виклику dash
), яка виконується лише перед тим, як вимагати нового вводу від користувача, у циклі REPL.
Таким чином, якщо оболонка заблокована в очікуванні на введення користувача, таке повідомлення не видається.
Тепер, чому перевірка, проведена відразу після вбивства, не показує, що процес був фактично припинений? Як пояснено вище, мабуть тому, що це занадто швидко. kill
- це внутрішня команда оболонки, тому її виконувати дуже швидко і не потрібно розгалуження, таким чином, коли відразу після kill
перевірки виконується процес, все ще живий (або, принаймні, все ще вбивається).
bash
Як і очікувалося, bash
будучи набагато складнішою оболонкою, було складніше і вимагало трохи gdb
-фу.
Назад, коли це повідомлення випромінюється, щось подібне
(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
Дзвінок, який перевіряє наявність мертвих робочих місць & co. є notify_of_job_status
(це більш-менш еквівалент showjobs(..., SHOW_CHANGED)
в dash
); № 0- # 1 пов'язані з її внутрішньою роботою; 6-8 - код, розроблений yacc; 10-12 - цикл REPL.
Цікаве місце тут №4, тобто звідки notify_and_cleanup
надходить дзвінок. Схоже, що bash
, на відміну від цього dash
, може перевіряти наявність припинених завдань на кожному символі, прочитаному з командного рядка, але ось що я знайшов:
/* 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 */
}
Отже, в інтерактивному режимі навмисно затримувати перевірку, поки не буде надано нове підказку, що, мабуть, не заважає користувачеві вводити команди. Що стосується того, чому перевірка не помічає мертвий процес під час відображення нового запиту відразу після kill
, попереднє пояснення має місце (процес ще не загинув).
pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"
але менше звичаю не з'являться