Чому баш показує "Припинено" після вбивства процесу?


17

Ось поведінку, яку я хочу зрозуміти:

$ ps
  PID TTY           TIME CMD
  392 ttys000    0:00.20 -bash
 4268 ttys000    0:00.00 xargs
$ kill 4268
$ ps
  PID TTY           TIME CMD
  392 ttys000    0:00.20 -bash
[1]+  Terminated: 15          xargs
$ ps
  PID TTY           TIME CMD
  392 ttys000    0:00.21 -bash

Чому це відображається [1]+ Terminated: 15 xargsпісля того, як я вбиваю процес, замість того, щоб просто не показувати його так, як він був просто вбитий?

Я використовую bash на Mac OS X 10.7.5.

Відповіді:


24

Коротка відповідь

У bashdash) різні повідомлення про "статус завдання" не відображаються від обробників сигналів, але вимагають чіткої перевірки. Ця перевірка виконується лише до того, як буде надано нове запит, імовірно, щоб не заважати користувачеві під час введення нової команди.

Повідомлення не відображається перед тим, як з'явиться запит після 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, попереднє пояснення має місце (процес ще не загинув).


5

Щоб уникнути будь-яких повідомлень про припинення роботи (як у командному рядку, так і у psвихідному), ви можете поставити команду для фонового зображення у sh -c 'cmd &'конструкцію.

{
ps
echo
pid="$(sh -c 'sleep 60 1>&-  & echo ${!}')"
#pid="$(sh -c 'sleep 60 1>/dev/null  & echo ${!}')"
#pid="$(sh -c 'sleep 60 & echo ${!}' | head -1)"
ps
kill $pid
echo
ps
}

До речі, можна негайно отримувати сповіщення про припинення роботи bashза допомогою параметрів оболонки set -bабо set -o notifyвідповідно.

У цьому випадку " bashнадходить SIGCHLDсигнал, і його обробник сигналу відображає повідомлення сповіщення негайно - навіть якщо bashзараз знаходиться в середині очікування завершення переднього плану" (див. Наступну посилання нижче).

Для отримання третього режиму сповіщення про управління роботою між set +b(режим за замовчуванням) та set -b(щоб ви отримали негайне сповіщення про припинення завдання, не пошкоджуючи те, що ви вже ввели у поточному командному рядку - подібне до ctrl-x ctrl-v), потрібен патч bashSimon Simon (для сам патч і додаткова інформація дивіться: Розумне асинхронне повідомлення роботи в БАШЕЄВ (1) ).

Тому просто повторимо Matteo Italia's gdb-fu для bashоболонки, яка була встановлена ​​для негайного повідомлення про припинення роботи set -b.

# 2 Terminal.app windows

# terminal window 1
# start Bash compiled with -g flag
~/Downloads/bash-4.2/bash -il
set -bm
echo $$ > bash.pid

# terminal window 2
gdb -n -q
(gdb) set print pretty on
(gdb) set history save on
(gdb) set history filename ~/.gdb_history
(gdb) set step-mode off
(gdb) set verbose on
(gdb) set height 0
(gdb) set width 0
(gdb) set pagination off
(gdb) set follow-fork-mode child
(gdb) thread apply all bt full
(gdb) shell cat bash.pid
(gdb) attach <bash.pid>
(gdb) break pretty_print_job

# terminal window 1
# cut & paste
# (input will be invisible on the command line)
sleep 600 &   

# terminal window 2
(gdb) continue
(gdb) ctrl-c

# terminal window 1
# cut & paste
kill $!

# terminal window 2
(gdb) continue
(gdb) bt

Reading in symbols for input.c...done.
Reading in symbols for readline.c...done.
Reading in symbols for y.tab.c...done.
Reading in symbols for eval.c...done.
Reading in symbols for shell.c...done.
#0  pretty_print_job (job_index=0, format=0, stream=0x7fff70bb9250) at jobs.c:1630
#1  0x0000000100032ae3 in notify_of_job_status () at jobs.c:3561
#2  0x0000000100031e21 in waitchld (wpid=-1, block=0) at jobs.c:3202
#3  0x0000000100031a1a in sigchld_handler (sig=20) at jobs.c:3049
#4  <signal handler called>
#5  0x00007fff85a9f464 in read ()
#6  0x00000001000b39a9 in rl_getc (stream=0x7fff70bb9120) at input.c:471
#7  0x00000001000b3940 in rl_read_key () at input.c:448
#8  0x0000000100097c88 in readline_internal_char () at readline.c:517
#9  0x0000000100097dba in readline_internal_charloop () at readline.c:579
#10 0x0000000100097de6 in readline_internal () at readline.c:593
#11 0x0000000100097842 in readline (prompt=0x100205f80 "noname:~ <yourname>$ ") at readline.c:342
#12 0x0000000100007ab7 in yy_readline_get () at parse.y:1443
#13 0x0000000100007bbe in yy_readline_get () at parse.y:1474
#14 0x00000001000079d1 in yy_getc () at parse.y:1376
#15 0x000000010000888d in shell_getc (remove_quoted_newline=1) at parse.y:2231
#16 0x0000000100009a22 in read_token (command=0) at parse.y:2908
#17 0x00000001000090c1 in yylex () at parse.y:2517
#18 0x000000010000466a in yyparse () at y.tab.c:2014
#19 0x00000001000042fb in parse_command () at eval.c:228
#20 0x00000001000043ef in read_command () at eval.c:272
#21 0x0000000100004088 in reader_loop () at eval.c:137
#22 0x0000000100001e4d in main (argc=2, argv=0x7fff5fbff528, env=0x7fff5fbff540) at shell.c:749

(gdb) detach
(gdb) quit

круто! але ти віриш, що там може бути інший шлях? Я намагаюсь це: pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"але менше звичаю не з'являться
Водолій Сила
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.