Rob*_*479 5 process io pipe file-descriptors fifo
尝试以下 shell 命令:
mkfifo /tmp/test.pipe
ls -1 /tmp > /tmp/test.pipe &
rm /tmp/test.pipe
mkfifo /tmp/test.pipe
cat /tmp/test.pipe &
jobs
Run Code Online (Sandbox Code Playgroud)
该ls
命令只是一个示例,可以是任何想要写入命名管道的进程。这里的重点是,该进程将阻止尝试打开管道进行写入。现在,另一个过程出现并移除管道。一个同名的新管道被创建,cat
进程(即任何试图打开它进行读取的进程)将阻塞等待另一个进程写入同名的新管道。两个进程都在等待各自的管道连接。这可以通过列出背景来验证jobs
。
我不太关心阻塞cat
(或任何试图从管道读取的进程),因为实际上从管道读取的进程实际上是首先替换它的进程。事实上,我根本不在乎阅读过程。将cat
仅用于我的例子来证明的确是又处理指的是同一管道的文件名,但显然是在利用不同的管道实例。但是,请注意,这个死锁示例在相反的情况下也会阻塞,即如果管道被删除而从未打开用于写入,则读取过程将永远阻塞。
我的重点是阻塞写入过程(ls
在我的示例中)。它永远没有机会意识到它试图写入的原始管道已被一个新的同名管道替换。
是否有任何 Linux 方法可以识别这种情况,即进程阻止写入对不再存在的命名管道的访问的情况?您可以假定具有 root 权限,并且我知道所有涉及的进程的 PID。特别是,我知道可能会或可能不会遇到这种情况的写作过程。但是,我不知道(这是我想知道的),该进程是否确实遇到了该问题,因此我可以终止它。如何找到阻止访问文件系统上物理上不再存在的命名管道的进程?
显然,ls /proc/<PID>/fd
不会列出任一进程对任一管道的访问的文件描述符,除非管道的两端都已连接,即ls /proc/<PID>/fd
只有在系统调用成功返回后,才会列出两个进程的管道的文件描述符它的结束。由于在我的示例中有两个不同的同名半连接管道,因此两个进程都没有列出。
正如Julie Pelletier所建议的,我正在对我们在讨论中找到的解决方法做出回答。
您无法轻松识别我的问题中描述的死锁情况,但您可以在任何人删除命名管道之前先发制人地释放命名管道作为解决方法(如果您确实无法避免这种删除,就像我的情况一样)。这种通风应该允许当前被阻止尝试打开管道的任何写入器成功打开操作,但在实际写入操作期间失败(-> 损坏的管道)。失败可能比陷入僵局要好。为了不立即陷入下一个僵局,您应该在排气之前移动管道,然后将其删除。
# rename the pipe, i.e. move it out of the way
mv -f /tmp/test.pipe /tmp/test.pipe~ 2>/dev/null
# vent the pipe, i.e. shortly open it for reading but don't read from it.
# call the subshell dd and empty echo calls in the background to avoid
# deadlocking on redundant venting.
(dd if=/tmp/test.pipe~ count=0 2>/dev/null & echo -n "" >/tmp/test.pipe~ &)
# delete the old pipe
rm -f /tmp/test.pipe~
Run Code Online (Sandbox Code Playgroud)
如果您知道哪个程序执行了错误的删除,您可能需要将该程序包装到一个小脚本中,该脚本会进行发泄并放弃删除。