杀死管道中的其他进程(以编程方式)

Ale*_*lls 2 kill pipe node.js

说我有这个:

node a.js | node b.js | node c.js
Run Code Online (Sandbox Code Playgroud)

如果 a.js 快要死了,我可以杀死 b.js 吗?反过来呢?似乎如果我建立这个管道,一个进程不一定会死/退出,如果另一个。如果一个人死了,我怎么能把他们都杀了?

Sté*_*las 5

在:

cmd1 | cmd2
Run Code Online (Sandbox Code Playgroud)

cmd2完成时不会自动死亡cmd1,但是,它会在其标准输入上看到文件结尾。这就是管道通常会走到尽头的方式。

在:

echo foo | sed s/o/e/g
Run Code Online (Sandbox Code Playgroud)

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

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

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

这就是管道的样子:

yes | head
Run Code Online (Sandbox Code Playgroud)

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

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

在 Linux 上,您可以/proc/*/fd在管道末端查找与 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]
Run Code Online (Sandbox Code Playgroud)

同一个管道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
Run Code Online (Sandbox Code Playgroud)

例子:

$ (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
Run Code Online (Sandbox Code Playgroud)

所以你可以这样做(仍然在 Linux 和 with 上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 Code Online (Sandbox Code Playgroud)

进而:

run_and_kill_the_other_end node a.js | run_and_kill_the_other_end node b.js
Run Code Online (Sandbox Code Playgroud)

尽管如此,这可能不是您想要做的。例如:

$ run_and_kill_the_other_end seq 100 |
    run_and_kill_the_other_end sort |
    run_and_kill_the_other_end wc -l
0
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

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