如果在未连接的情况下删除管道,则永久打开命名管道块

如果在未连接的情况下删除管道,则永久打开命名管道块

尝试以下 shell 命令:

mkfifo /tmp/test.pipe

ls -1 /tmp > /tmp/test.pipe &

rm /tmp/test.pipe
mkfifo /tmp/test.pipe
cat /tmp/test.pipe &

jobs

ls命令只是一个示例,可以是任何想要写入命名管道的进程。这里的要点是,该进程将阻止尝试打开管道进行写入。现在,另一个过程出现并移除管道。创建同名的新管道,并且cat进程(即尝试打开它进行读取的任何进程)将阻塞等待另一个进程写入同名的新管道。两个进程都在等待各自的管道连接。这可以通过列出背景来验证jobs

我不太关心阻塞cat(或任何试图从管道读取的进程),因为实际上从管道读取的进程实际上是首先替换它的进程。事实上,我根本不关心阅读过程。唯一cat的例子是证明两个进程确实引用相同的管道文件名,但显然使用不同的管道实例。然而,请注意,这个死锁示例在相反的情况下也会阻塞,即,如果管道在没有打开写入的情况下被删除,则读取过程将永远阻塞。

我的重点是阻塞写入过程(ls在我的示例中)。它永远没有机会意识到它试图写入的原始管道已被同名的新管道替换。

Linux 是否有任何方法可以识别这种情况,即进程阻止对不再存在的命名管道进行写入访问的情况?您可以假定 root 权限,并且我知道所涉及的所有进程的 PID。特别是我知道写作过程中可能会也可能不会遇到这样的情况。但是,我不知道(这就是我想知道),这个过程是否真的遇到了这个问题,这样我就可以终止它。如何找到阻止访问文件系统上物理上不再存在的命名管道的进程?

显然,ls /proc/<PID>/fd不会列出任一进程对任一管道的访问的文件描述符,除非管道的两端都已连接,即ls /proc/<PID>/fd仅在系统调用成功返回两个进程后才会列出两个进程的管道的文件描述符它的结束。因为在我的例子中有两个不同的半连接管道名称相同,但两个进程都没有列出。

答案1

正如建议的朱莉·佩尔蒂埃,我正在对我们在讨论中找到的解决方法做出这个回答。

您无法轻松识别我的问题中描述的僵局情况,但您可以先发制人发泄命名管道作为任何人删除它之前的解决方法(如果您确实无法避免这样的删除,就像我的情况一样)。这种通风应该允许当前被阻止尝试打开管道的任何写入器成功打开操作,但在实际写入操作期间失败(-> 损坏的管道)。失败可能比陷入僵局要好。为了不立即陷入下一个僵局,您应该在排气之前移动管道,然后将其删除。

# 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~

如果您知道哪个程序执行了错误的删除,您可能需要将该程序包装到一个小脚本中,该脚本会进行发泄并放弃删除。

相关内容