I can only presume that the signal of the first element in the pipeline is propagated through to the rest of the elements in the pipeline.
Até onde eu sei, não existe tal propagação. Eu vou responder principalmente a sua primeira pergunta:
What is actually going on?
Resposta curta
(Isso pode ser um pouco simplificado.)
- Ao executar um canal,
bash
coloca cada processo em um grupo de processos comPGID
(ID do grupo de processos) igual aoPID
(ID do processo) do primeiro comando. -
timeout
altera seu próprioPGID
para seu próprioPID
. Isso não muda nada setimeout
for o primeiro comando no canal. -
timeout
envia o sinal não apenas para o comando subjacente, mas também para todo o seu grupo de processos. Setimeout
for o primeiro comando no pipeline, o grupo de processos ainda incluirágpg
, portanto,gpg
obterá o sinal.
O fenômeno é pesquisado e elaborado abaixo.
Elaboração
1. bash
behavior
Ao executar um canal, bash
coloca cada processo em um grupo de processos com PGID
igual ao PID
do primeiro comando. Você pode fazer seus próprios testes (consulte É possível obter o ID do grupo de processos de /proc?
). Eu não pesquisei possibilidades mais complexas (por exemplo, se o primeiro "comando" é um subnível?), No seu caso eles não importam. O que importa é que gpg
nesses comandos
timeout 1 cat /dev/urandom | gpg -er [email protected] > ./myfile.gpg
cat /dev/urandom | timeout 1 cat | gpg -er [email protected] > ./myfile.gpg
timeout -sUSR1 1 cat /dev/urandom | gpg -er [email protected] > ./myfile.gpg
gpg -er [email protected] > ./myfile.gpg < <( timeout 1 cat /dev/urandom )
obtém PGID
igual ao PID
de
-
timeout
- (o primeiro)
cat
-
timeout
-
gpg
(ou seja, ela própria)
respectivamente.
2. timeout
altera seu próprio PGID
(ou não)
Execute strace timeout 1 cat
e você verá, entre outras coisas:
setpgid(0, 0)
Um trecho de man 2 setpgid
:
int setpgid(pid_t pid, pid_t pgid);
setpgid()
sets thePGID
of the process specified bypid
topgid
. Ifpid
is zero, then the process ID of the calling process is used. Ifpgid
is zero, then thePGID
of the process specified bypid
is made the same as its process ID.
Isso significa que timeout
define seu PGID
igual a seu PID
. Existem duas possibilidades:
- se
timeout
for o primeiro comando, seuPGID
será o mesmo antes e depois desetpgid
, entãogpg
ainda terá o mesmoPGID
detimeout
; - se
timeout
não for o primeiro comando, seuPGID
será alterado e mesmo quegpg
tenha inicialmente o mesmoPGID
detimeout
, os doisPGID
s serão diferentes agora.
3. timeout
envia mais sinais do que você esperava
O mesmo strace timeout 1 cat
revela linhas como:
kill(19401, SIGTERM)
…
kill(0, SIGTERM)
Neste exemplo, 19401
é o PID
de cat
. Se você usou -s USR1
, então haverá SIGUSR1
em vez de SIGTERM
etc. Esse segundo kill
é responsável pelo que você pensou ser uma propagação de sinal através do pipeline. Veja man 2 kill
(excerto):
int kill(pid_t pid, int sig);
If
pid
equals0
, thensig
is sent to every process in the process group of the calling process.
O processo de chamada é timeout
. Ele envia sinais para todo o seu grupo de processos. Eu admito que não sei qual é o propósito por trás disso, ainda acontece.
Portanto, se timeout
for o primeiro comando no pipeline, o sinal escolhido será enviado para cada parte dele (bem, quase; considere outro timeout
no mesmo pipeline). Isso inclui gpg
. Então cabe a gpg
como ele reage ao sinal.
Outras perguntas
Do we have any control over this? Is there a better way than moving the signal-generating-process from the front of the pipeline?
Minha pesquisa rápida não gerou nenhuma ferramenta comum para definir / alterar PGID
. Eu acho que você pode escrever seu próprio programa que irá chamar setpgid(2)
ou mais; mas agora, quando sabemos o que está acontecendo, mover timeout
da frente do pipeline parece ser uma abordagem bastante sensata.
Observe também que isso ocorre devido a como timeout
se comporta. Outros processos geradores de sinal podem não precisar dessa solução alternativa.