Mate outros processos na linha de tubulação (programaticamente)

2

Digamos que eu tenha isto:

node a.js | node b.js | node c.js

Se a.js estiver prestes a morrer, posso matar o b.js? E quanto ao vice-versa? Parece que se eu estabelecer este tubo, um processo não irá necessariamente morrer / sair se o outro fizer. Como posso matá-los todos se morre?

    
por Alexander Mills 20.09.2017 / 23:37

1 resposta

5

Em:

cmd1 | cmd2

cmd2 não morre automaticamente quando cmd1 termina, no entanto, ele verá o final do arquivo em seu stdin. E é assim que os pipelines costumam chegar ao fim.

Em:

echo foo | sed s/o/e/g

sed será encerrado após echo ter terminado, porque descobre que não há mais a ler de seu stdin.

Você poderia tentar e ter cmd1 kill cmd2 na finalização, mas e se cmd2 não tivesse lido tudo que cmd1 escreveu no pipe ainda?

Se cmd2 morrer primeiro, cmd1 não morrerá automaticamente, mas será morto (com um SIGPIPE) na próxima vez que tentar gravar no canal (agora quebrado).

É assim que os pipelines gostam:

yes | head

terminate ( yes é eliminado após a primeira gravação que faz no pipe após head ter terminado depois de ler e imprimir as primeiras 10 linhas).

Agora, se você realmente quisesse matar processos na outra extremidade do canal, não há nenhuma maneira portátil de descobrir quais processos têm um descritor de arquivos para a outra extremidade de um canal.

No Linux, você pode procurar os arquivos em /proc/*/fd que têm o mesmo inode do pipe no fd até o final do pipe e onde as permissões do symlink em si indicam qual é o final do pipe.

$ (ls -lLi /proc/self/fd/3; ls -l /proc/self/fd/3) 3>&1 >&2 | (ls -Lil /proc/self/fd/0; ls -l /proc/self/fd/0)
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/3|
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/0|
l-wx------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/3 -> pipe:[224052]
lr-x------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/0 -> pipe:[224052]

O mesmo pipe inode e o fd até o fim da escrita tem w de permissões, enquanto o que está no final da leitura tem r permissões.

Por exemplo, com zsh você pode obter os pids dos processos que possuem um fd na extremidade de leitura de um canal cuja outra extremidade está em nosso fd 3 com:

pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
pids=(${(u)pids}) # unique

Exemplo:

$ (pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
  pids=(${(u)pids}); ps -fp $pids) 3>&1 >&2 | tr a b
UID        PID  PPID  C STIME TTY          TIME CMD
chazelas 24157 11759  0 23:41 pts/1    00:00:00 tr a b

Então você pode fazer (ainda no Linux e com zsh ):

run_and_kill_the_other_end() {
  setopt localoptions localtraps
  trap : TERM
  "$@"
  local ret=$?
  local -aU pids
  if [ -p /dev/stdout ]; then
    pids=(/proc/<2->/fd/<0-9>(Nf{u+r}e{'[[ $REPLY -ef /dev/stdout ]]'}-p:h:h:t))
    exec >&- # give the right end a chance to see eof and act upon it
  fi
  [ -p /dev/stdin ] &&
    pids+=(/proc/<2->/fd/<0-9>(Nf{u+w}e{'[[ $REPLY -ef /dev/stdin ]]'}-p:h:h:t))
  (($#pids)) && kill $pids 2> /dev/null
  return $ret
}

E então:

run_and_kill_the_other_end node a.js | run_and_kill_the_other_end node b.js

Mais uma vez, provavelmente não é isso que você quer fazer. Por exemplo:

$ run_and_kill_the_other_end seq 100 |
    run_and_kill_the_other_end sort |
    run_and_kill_the_other_end wc -l
0

sort foi morto antes de ter a chance de escrever sua saída classificada. wc -l conseguiu escapar.

Você pode inserir um atraso como período de tolerância, mas por quanto tempo deve ser o atraso? Por exemplo, se eu adicionar um sleep 0.1 após o exec >&- , vejo:

$ run_and_kill_the_other_end seq 100 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
100
$ run_and_kill_the_other_end seq 10000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
10000
$ run_and_kill_the_other_end seq 100000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
0

Isso também significa atrasar o término do pipeline incondicionalmente. Você pode melhorar isso usando zsh de zselect com um tempo limite.

    
por 21.09.2017 / 00:09