プロセスを終了した後にbashに「終了しました」と表示されるのはなぜですか?

プロセスを終了した後にbashに「終了しました」と表示されるのはなぜですか?

これは私が理解したい行動です。

$ 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プロセスが終了したときに表示されず、プロセスが終了した後に表示されるのはなぜですか?

私はMac OS X 10.7.5でbashを使用しています。

答え1

短い答え

bash(および)では、dashさまざまな「ジョブステータス」メッセージがシグナルハンドラに表示されませんが、明示的なチェックが必要です。この確認は、新しいプロンプトを入力する前にのみ実行され、ユーザーが新しいコマンドを入力している間にユーザーを邪魔しないことがあります。

メッセージは、kill表示後にプロンプ​​トの前に表示されません。それはおそらく、まだプロセスが終了していないからです。これは特にkillシェルの内部コマンドなので、非常に速く実行され、フォークが不要なので、そうする可能性が高くなります。

対照的に、.exeを使用して同じ実験を実行すると、通常は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-fuが必要です。

このメッセージが発行されたときのトレースは次のとおりです。

(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

死んだ仕事と会社の電話番号を確認してください。 is ( innotify_of_job_statusとほぼ同じ ) #0-#1 は内部動作に関連しており、6-8 は yacc 生成パーサコードです。showjobs(..., SHOW_CHANGED)dash

ここで興味深いのは、notify_and_cleanup呼び出しが発生する#4です。これとは異なり、コマンドラインから読み取ったすべての文字で終了した操作を確認することは可能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ます(プロセスはまだ死んでいません)。

答え2

コマンドラインと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(現在のコマンドラインにすでに入力されているものを削除せずにすぐにジョブ終了通知を受け取るようにジョブ制御通知の3番目のモードを取得するには、Tathamなどのパッチctrl-x ctrl-vbashパッチ自体などにパッチを適用)情報を参照してください。Bashの合理的な非同期タスク通知(1))。

Matteo Italiaしたがって、ジョブの終了をすぐに通知するように設定されたシェルに対して-fuを繰り返してみましょう。gdbbashset -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

関連情報