프로세스를 종료한 후 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 ( in notify_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(현재 명령줄에 이미 입력한 내용을 삭제하지 않고 즉시 작업 종료 알림을 받을 수 있도록 작업 제어 알림의 세 번째 모드를 받으려면 Tatham과 같은 ctrl-x ctrl-v패치( bash패치 자체 등에 대해 패치 적용) 정보 참조: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

관련 정보