终止管道中的其他进程(以编程方式)

终止管道中的其他进程(以编程方式)

假设我有这个:

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

如果a.js快死了,我可以杀掉b.js吗?反之又如何呢?似乎如果我建立这个管道,一个进程不一定会在另一个进程死亡/退出时死亡/退出。如果一个人死了,我怎么能把他们全部杀掉呢?

答案1

在:

cmd1 | cmd2

cmd2完成后不会自动终止cmd1,但是,它会在其标准输入上看到文件结尾。管道通常就是这样结束的。

在:

echo foo | sed s/o/e/g

sed将在终止后退出echo,因为它发现此时没有更多内容可从其标准输入中读取。

您可以尝试在终止时cmd1终止cmd2,但如果cmd2尚未读取cmd1写入管道的所有内容怎么办?

如果cmd2先死,cmd1则不会自动死,但下次尝试写入(现已损坏的)管道时,它将被杀死(使用 SIGPIPE)。

管道就是这样的:

yes | head

终止(在读取和打印前 10 行后终止后,yes第一次写入管道时被终止)。head

现在,如果您确实想终止管道另一端的进程,则没有可移植的方法来找出哪些进程具有管道另一端的文件描述符。

/proc/*/fd在 Linux 上,您可以在管道末端的 fd 上查找与管道具有相同 inode 的文件,并且符号链接本身的权限指示它是管道的哪一端。

$ (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]

同一个管道inode,写端的fd有w权限,读端的fd有r权限。

例如,zsh您可以使用以下命令获取在管道读取端具有 fd 的进程的 pid,该管道的另一端位于我们的 fd 3 上:

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

例子:

$ (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

所以你可以这样做(仍然在Linux上并使用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
}

进而:

run_and_kill_the_other_end node a.js | run_and_kill_the_other_end node b.js

不过,这可能不是您想要做的。例如:

$ 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在有机会写入排序输出之前就被杀死了。wc -l设法逃脱。

您可以插入延迟作为宽限期,但延迟应该多长?例如,如果我在sleep 0.1后面添加exec >&-,我会看到:

$ 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

这也意味着无条件延迟管道的终止。您可以通过使用带有超时的zsh's来改进这一点。zselect

相关内容