读取手册页关闭,如果它被信号中断,那么fd的状态是未指定的。是否有处理此案例的最佳做法,或者之后是否会成为操作系统的问题。
我认为EIO在适当关闭fd后失败。
答案 0 :(得分:1)
如果您希望程序运行很长时间,可能的文件描述符泄漏绝不仅仅是操作系统的问题。当然,不使用许多文件描述符的短期程序可以选择终止未闭合的描述符,并且在程序终止时依赖于内核关闭它们。因此,对于我的其余部分,我将假设您的程序已经运行很长时间。
如果您的程序不是多线程的,那么您将面临一个非常简单的情况:
int close_some_fd(int fd, int *was_interrupted) {
*was_interrupted = 0;
/* this is point X to which I will draw your attention later. */
for (;;) {
if (0 == close(fd))
return 0; /* Success. */
if (EIO != errno)
return -1; /* Some failure, not interrupted by a signal. */
/* Our close attempt was interrupted. */
*was_interrupted = 1;
int fdflags = 0;
/* Just use the fd to find out if it is still open. */
if (0 != fcntl(fd, F_GETFD, &fdflags))
return 0; /* Interrupted, but file is also closed. So we are done. */
}
}
另一方面,如果您的代码是多线程的,那么其他一些线程(可能是您无法控制的线程,例如名称服务缓存)可能已调用dup
,dup2
,socket
,open
,accept
或其他一些使内核分配文件描述符的类似函数。
要使类似的方法在这样的环境中工作,您需要能够分辨出您启动的文件描述符与另一个线程新打开的文件描述符之间的区别。知道已经存在一个仍然没有打开的编号较低的fd足以打折那些,但在多线程环境中你没有一个简单的方法来确定这仍然是这种情况。 / p>
一种选择是依赖程序使用的所有文件描述符的一些常见方面。例如,如果它从不使用O_CLOEXEC
,您可以使用fcntl
在代码中标记为X
的点处设置O_CLOEXEC标志,然后您只需将现有调用更改为{{1像这样:
fcntl
您可以调整此方法,以使用 if (0 = fcntl(fd, F_GETFD, &fdflags)) {
if (fdflags & O_CLOEXEC) {
/* open, and still marked with O_CLOEXEC, unused elsewhere. */
continue;
} else {
return 0; /* Interrupted, but file is also closed. So we are done. */
}
}
以外的其他内容(例如,O_CLOEXEC
,记录fstat
和st_dev
),但如果您无法确定你的多线程程序的其余部分正在做什么,这个一般性的想法很可能不会令人满意。
另一种方法也有点问题,但可能有用。这就是,不是关闭文件描述符,而是使用st_ino
将文件描述符通过Unix域套接字传递给单独的,单线程的专用服务器,其唯一的工作就是关闭文件描述符。是的,这有点icky。这种方法的一些有趣的属性是:
sendmsg
时阻止信号传递。但是,它意味着每个文件描述符关闭的用户空间上下文切换开销(除非您将它们分批以分摊成本)。您需要避免线程A可能正在读取与线程B发出的请求相对应的fd-closed-OK报告的情况。您可以通过序列化关闭操作(这将限制性能)或解复用响应来避免该问题(这是复杂)。或者,您可以使用其他一些IPC机制来避免序列化或解复用(例如SYSV信号量)。至于我在最常用的应用程序中做什么,我会弄清楚我可以在多大程度上对我正在关闭的文件描述符 kind 做出假设。例如,如果它们通常是套接字,我只是尝试使用测试程序,并确定sendmsg
是否通常使文件描述符保持关闭状态。也就是说,确定EIO
上的文件描述符的理论上未指定的状态在实践中是否是可预测的。如果fd可以是任何东西(例如磁盘文件,套接字,tty,......),这将无法正常工作。当然,如果您使用的是某些开源操作系统,您可能只能读取内核源代码并确定可能的结果。
当然,我会尝试使用上面基于实验的系统,然后再担心将fd-close服务器分解为fd-closing的缩小。