【发布时间】:2014-02-07 20:45:03
【问题描述】:
我正在为 Linux 编写一个 C 程序,用于读取和写入 NFS 服务器上的文件。共享是硬挂载的;访问它的尝试将无限期地阻塞,直到它们工作。无限期地阻止我的程序是不好的。即使文件不可用,它仍然能够做有用的工作。重新安装共享软件不是一种选择。
有两个进程,其中一个可以工作并且不会阻塞,另一个处理文件 IO 并且可能会阻塞是一个选项,但会构成重大更改。我想避免这种情况。真的,我想说,“我知道你是硬挂载的,所以天真的程序可以假装你是一个高度可靠的本地磁盘。但我知道得更好,我准备应对任何访问失败,类似于行为如果你是软装的话。”所以:
在 C 中,我如何访问硬挂载的 NFS 共享上的文件,如果服务器不可用而不是无限期地阻塞,则会出现错误?
如有必要,我可以以 root 身份运行,但不希望如此。使用 root 重新安装共享是正确的。我可以潜在地依赖新功能,但越往后支持越好。
我的研究表明答案是不可能的,但也许我错过了一些东西。
【问题讨论】:
-
为什么不设置一个超时信号在合适的超时后中断阻塞调用。每当您进行阻塞调用(
open()、fopen()、readdir()、chdir()、stat()等)时启动计时器,如果调用返回errno==EINTR,并且设置了超时信号处理程序一个标志,您可以判断呼叫在分配的时间内没有进行,并将其视为呼叫出错。超时信号处理的额外开销是值得的;这是 Apache 等服务器软件的常见做法。将超时设置为用户可配置,然后您就设置好了。 -
这是一个潜在的解决方法。这可靠吗?我相信不可用的 NFS 挂载可能会使进程进入不可中断的睡眠状态,而信号本身会阻止这种睡眠。
-
啊。在 2.6.25 之前的内核
nointr挂载选项确实使它可靠,但在以后的内核中它被忽略了。SIGKILL是唯一有效的方法。因此,您需要使用子进程来访问挂载,并通过 Unix 域套接字辅助消息返回描述符(文件或目录)。如果操作时间过长,原始进程会使用超时KILL子进程。使用为每个操作派生一个子子的长寿命最小子应将开销降低到可接受的水平。也许在每次访问之前解析/proc/mounts也只能绕过 NFS。 -
实际上,您必须让子(或子子)提供数据流,因为文件系统随时可能无法访问。仅提供描述符只能保护打开,而不是访问,以免陷入不间断的睡眠。此外,超时会稍微复杂一些:一个用于初始访问,另一个用于每个单独的数据包。