Bir işlemi öldürdükten sonra bash neden 'Sonlandırıldı' gösteriyor?


17

İşte anlamak istediğim davranış:

$ 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

Neden [1]+ Terminated: 15 xargsbir süreci öldürdüğümden sonra, sadece öldürüldüğü gibi göstermektense gösteriyor?

Mac OS X 10.7.5'te bash kullanıyorum.

Yanıtlar:


24

Kısa cevap

In bash(ve dash) çeşitli "iş durumu" mesajları sinyal işleyici görüntülenen, ancak açık bir kontrol yapılmasını gerektirir değildir. Bu kontrol yalnızca yeni bir komut istemi yapılmadan önce yapılır, muhtemelen yeni bir komut yazarken kullanıcıyı rahatsız etmemelidir.

killMuhtemelen, işlem henüz ölmediği için görüntülenmesinden hemen sonraki istemden önce gösterilmez - killkabuğun dahili bir komutu olduğu için bu özellikle olası bir durumdur , bu nedenle yürütmek çok hızlıdır ve çatallamaya gerek yoktur.

Bunun killallyerine, aynı deneyi yapmak genellikle hemen "öldürülmüş" mesajını verir, zaman / bağlam anahtarlarının / harici bir komutun yürütülmesi için gereken her şeyin, kontrol kabuğa dönmeden önce işlemin öldürülmesi için yeterince uzun bir gecikmeye neden olduğunu belirtin .

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
$ 

Uzun cevap

dash

Her şeyden önce, aynı davranışı sergilediği ve kod kesinlikle daha basit olduğu için, dashkaynaklara bir göz vardı .dashbash

Yukarıda belirtildiği gibi, nokta, iş durumu mesajlarının bir sinyal işleyicisinden ("normal" kabuk kontrol akışını kesintiye uğratabilecek şekilde) yayılmadığı, ancak yapılan açık bir kontrolün (bir showjobs(out2, SHOW_CHANGED)çağrı girişi dash) sonucudur. yalnızca kullanıcıdan yeni girdi talep etmeden önce, REPL döngüsünde.

Böylece, kullanıcı girişi beklenirken kabuk engellenirse böyle bir mesaj gönderilmez.

Şimdi, öldürmeden hemen sonra yapılan kontrol neden sürecin gerçekten sonlandığını göstermiyor? Yukarıda açıklandığı gibi, muhtemelen çok hızlı olduğu için. killkabuğun dahili bir komutudur, bu nedenle yürütülmesi çok hızlıdır ve çatallamaya gerek yoktur, bu nedenle, killkontrol gerçekleştirildikten hemen sonra , süreç hala hayatta (veya en azından hala öldürülüyor).


bash

Beklendiği gibi, bashçok daha karmaşık bir kabuk olmak, daha gdbhileli ve biraz -fu gerekiyordu.

Bu mesajın yayıldığı zamanki iz,

(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

Ölü işleri ve eşleri kontrol eden çağrı. olduğu notify_of_job_status(daha fazla veya daha az eşdeğer showjobs(..., SHOW_CHANGED)olarak dash); # 0- # 1 iç çalışmasıyla ilgilidir; 6-8 yacc tarafından üretilen ayrıştırıcı kodudur; 10-12 REPL döngüsüdür.

Buradaki ilginç yer # 4, yani notify_and_cleanupçağrının geldiği yer. Görünüşe göre bash, dashkomut satırından okunan her karakterde sonlandırılmış işleri kontrol edebilir, ancak bulduğum şey:

      /* 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 */
    }

Bu nedenle, etkileşimli modda , muhtemelen kullanıcının komutları girmesini rahatsız etmemek için yeni bir bilgi istemi sağlanana kadar denetimi ertelemek niyetlidir . Kontrolün hemen ardından yeni istemi görüntülerken denetimin ölü işlemi fark etmemesine gelince kill, önceki açıklama geçerlidir (işlem henüz ölmedi).


5

İş sonlandırma iletilerinden (komut satırında ve psçıktıda) kaçınmak için, arka planlanacak komutu bir sh -c 'cmd &'yapıya koyabilirsiniz .

{
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
}

Bu arada, bashkabuk seçeneklerini kullanarak set -bveya set -o notifysırasıyla iş sonlandırma bildirimleri almak mümkündür .

Bu durumda " bashbir SIGCHLDsinyal alır ve sinyal işleyicisi bash, şu anda bir ön plan işleminin tamamlanmasını beklemenin ortasında olsa bile bildirim iletisini hemen görüntüler " (aşağıdaki bir sonraki referansa bakın).

Üçüncü bir iş denetimi bildirimi almak için set +b(varsayılan mod) ve set -b(böylece geçerli komut satırınıza zaten yazdığınız şeyi bozmadan hemen iş sonlandırma bildirimleri almanız için ctrl-x ctrl-v) benzer olan bashSimon Tatham ( düzeltme ekinin kendisi ve daha fazla bilgi için bkz . bash (1) ) içindeki asenkron iş bildirimi .

Öyleyse , iş sonlandırmayı derhal bildirmek üzere ayarlanmış bir kabuk için Matteo Italia's gdb-fu'yu tekrarlayalım .bashset -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

güzel! ama başka yolların olabileceğine inanıyor musun? Bunu deniyorum: pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"ama daha az görünmeyecek
Kova Gücü
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.