Resposta curta
Em bash
(e dash
) as várias mensagens de "status do trabalho" não são exibidas dos manipuladores de sinais, mas exigem uma verificação explícita. Essa verificação é executada apenas antes de um novo prompt ser fornecido, provavelmente, para não perturbar o usuário enquanto ele está digitando um novo comando.
A mensagem não é mostrada logo antes do prompt após o kill
ser exibido provavelmente porque o processo ainda não está morto - essa é uma condição particularmente provável, pois kill
é um comando interno do shell, portanto é muito rápido executar e não precisa de bifurcação.
Em vez disso, fazer a mesma experiência com killall
geralmente produz a mensagem "kill" imediatamente, sinal de que as alterações de hora / contexto / o que for necessário para executar um comando externo causam um atraso suficiente para que o processo seja morto antes o controle retorna ao 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
$
Resposta longa
dash
Primeiro de tudo, eu dei uma olhada nas fontes dash
, já que dash
exibe o mesmo comportamento e o código é certamente mais simples que bash
.
Como dito acima, o ponto parece ser que as mensagens de status do trabalho não são emitidas de um manipulador de sinal (que pode interromper o fluxo de controle de shell "normal"), mas são a conseqüência de uma verificação explícita (a showjobs(out2, SHOW_CHANGED)
chamada em dash
) que é executada somente antes de solicitar nova entrada do usuário, no loop REPL.
Assim, se o shell estiver bloqueado aguardando a entrada do usuário, nenhuma mensagem será emitida.
Agora, por que a checagem realizada logo após a morte mostra que o processo foi realmente encerrado? Como explicado acima, provavelmente porque é muito rápido. kill
é um comando interno do shell, portanto é muito rápido de executar e não precisa de bifurcação, portanto, quando imediatamente após a execução da verificação, o processo ainda está ativo (ou, pelo menos, é ainda está sendo morto).
kill
Como esperado, bash
, sendo um shell muito mais complexo, foi mais complicado e exigiu alguns bash
-fu.
O backtrace para quando essa mensagem é emitida é algo como
(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
A chamada que verifica empregos mortos & co. é gdb
(é mais ou menos o equivalente a notify_of_job_status
in showjobs(..., SHOW_CHANGED)
); # 0- # 1 estão relacionados ao seu funcionamento interno; 6-8 é o código do analisador gerado pelo yacc; 10-12 é o loop REPL.
O local interessante aqui é # 4, ou seja, de onde vem a chamada dash
. Parece que notify_and_cleanup
, ao contrário de bash
, pode verificar se há trabalhos terminados em cada caractere lidos na linha de comando, mas aqui está o que eu encontrei:
/* 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 */
}
Portanto, no modo interativo, é intencional atrasar a verificação até que um novo prompt seja fornecido, provavelmente para não atrapalhar o usuário a inserir comandos. Quanto ao motivo pelo qual a verificação não identifica o processo morto ao exibir o novo prompt imediatamente após o dash
, a explicação anterior é válida (o processo ainda não está morto).