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,
bashcoloca cada processo em um grupo de processos comPGID(ID do grupo de processos) igual aoPID(ID do processo) do primeiro comando. -
timeoutaltera seu próprioPGIDpara seu próprioPID. Isso não muda nada setimeoutfor o primeiro comando no canal. -
timeoutenvia o sinal não apenas para o comando subjacente, mas também para todo o seu grupo de processos. Setimeoutfor o primeiro comando no pipeline, o grupo de processos ainda incluirágpg, portanto,gpgobterá 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 thePGIDof the process specified bypidtopgid. Ifpidis zero, then the process ID of the calling process is used. Ifpgidis zero, then thePGIDof the process specified bypidis made the same as its process ID.
Isso significa que timeout define seu PGID igual a seu PID . Existem duas possibilidades:
- se
timeoutfor o primeiro comando, seuPGIDserá o mesmo antes e depois desetpgid, entãogpgainda terá o mesmoPGIDdetimeout; - se
timeoutnão for o primeiro comando, seuPGIDserá alterado e mesmo quegpgtenha inicialmente o mesmoPGIDdetimeout, os doisPGIDs 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
pidequals0, thensigis 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.