Risposta breve
Nei bash
(e dash
) i vari messaggi di "stato del lavoro" non vengono visualizzati dai gestori dei segnali, ma richiedono un controllo esplicito. Questo controllo viene eseguito solo prima che venga fornito un nuovo prompt, probabilmente per non disturbare l'utente mentre sta digitando un nuovo comando.
Il messaggio non viene mostrato poco prima del prompt dopo che kill
viene visualizzato probabilmente perché il processo non è ancora morto - questa è una condizione particolarmente probabile poiché kill
è un comando interno della shell, quindi è molto veloce da eseguire e non necessita di biforcazione.
Fare lo stesso esperimento con killall
, invece, di solito produce immediatamente il messaggio "ucciso", segno che i cambi di tempo / contesto / qualunque cosa sia necessaria per eseguire un comando esterno causano un ritardo abbastanza lungo da far terminare il processo prima che il controllo ritorni alla shell .
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
$
Risposta lunga
dash
Prima di tutto, ho dato un'occhiata alle dash
fonti, poiché dash
mostra lo stesso comportamento e il codice è sicuramente più semplice di bash
.
Come detto sopra, il punto sembra essere che i messaggi sullo stato del lavoro non vengono emessi da un gestore di segnale (che può interrompere il flusso di controllo della shell "normale"), ma sono la conseguenza di un controllo esplicito (una showjobs(out2, SHOW_CHANGED)
chiamata in dash
) che viene eseguito solo prima di richiedere un nuovo input dall'utente, nel ciclo REPL.
Pertanto, se la shell viene bloccata in attesa dell'input dell'utente, tale messaggio non viene emesso.
Ora, perché il controllo non viene eseguito subito dopo l'uccisione mostra che il processo è stato effettivamente terminato? Come spiegato sopra, probabilmente perché è troppo veloce. kill
è un comando interno della shell, quindi è molto veloce da eseguire e non necessita di biforcazione, quindi, quando immediatamente dopo kill
il controllo viene eseguito, il processo è ancora vivo (o, almeno, viene ancora ucciso).
bash
Come previsto, bash
essendo un guscio molto più complesso, era più complicato e richiedeva un po 'di gdb
fortuna.
Il backtrace per quando viene emesso quel messaggio è qualcosa di simile
(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
La chiamata che controlla i posti di lavoro morti e altri. è notify_of_job_status
(è più o meno l'equivalente di showjobs(..., SHOW_CHANGED)
in dash
); # 0- # 1 sono collegati al suo funzionamento interiore; 6-8 è il codice parser generato da yacc; 10-12 è il ciclo REPL.
Il posto interessante qui è il n. 4, ovvero da dove notify_and_cleanup
proviene la chiamata. Sembra che bash
, a differenza dash
, possa verificare la presenza di lavori terminati su ciascun personaggio letto dalla riga di comando, ma ecco cosa ho trovato:
/* 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 */
}
Pertanto, in modalità interattiva è intenzionale ritardare il controllo fino a quando non viene fornito un nuovo prompt, probabilmente per non disturbare l'utente a immettere i comandi. Per quanto riguarda il motivo per cui il controllo non individua il processo morto quando si visualizza il nuovo prompt immediatamente dopo il kill
, la spiegazione precedente vale (il processo non è ancora morto).
pid="$(sh -c 'cat "$fileName" |less & echo ${!}')"
ma meno non si presenta