POSIX / UNIX:如何可靠地关闭文件描述符

时间:2016-10-26 08:41:55

标签: c++ multithreading unix freebsd

问题:

在使用EINTR或EIO失败的close()系统调用之后,未指定文件是否已关闭。 (http://pubs.opengroup.org/onlinepubs/9699919799/) 在多线程应用程序中, 重试关闭可能会关闭其他线程打开的不相关文件。不重试关闭可能导致无法打开的文件描述符堆积。一个干净的解决方案可能涉及在新近关闭的文件描述符上调用fstat()和一个非常复杂的锁定机制。 此外,使用单个互斥锁序列化所有打开/关闭/接受/ ...调用可能是一种选择。

这些解决方案没有考虑到这一点 库函数可以以不可控制的方式自己打开和关闭文件,例如std :: thread :: hardware_concurrency()的一些实现在/ proc文件系统中打开文件。

[file.streams] C ++标准部分中的文件流不是一个选项。

在存在多个线程的情况下,是否有一种简单可靠的机制来关闭文件?

编辑:

常规文件: 虽然大多数时候没有不可用的打开文件描述符累积,但两个条件可能会触发问题: 1.某些恶意软件以高频率发出的信号 2.刷新高速缓存之前丢失连接的网络文件系统。

套接字:根据Stevens / Fenner / Rudoff的说法,如果套接字选项SO_LINGER设置在引用连接套接字的文件描述符上,并且在close()期间,定时器在FIN-ACK关闭序列完成之前经过,则关闭()作为通用程序的一部分失败。 Linux没有显示这种行为,但FreeBSD会这样做,并且还将errno设置为EAGAIN。据我所知,在这种情况下,未指定文件描述符是否无效。用于测试行为的C ++代码:http://www.longhaulmail.de/misc/close.txt那里的测试代码输出看起来像FreeBSD中的竞争条件,如果不是,为什么不呢?

有人可能会在调用close()期间考虑阻止信号。

3 个答案:

答案 0 :(得分:6)

此问题已在POSIX中修复以用于下一期;不幸的是,进入最近的TC2变化太大了。有关the final accepted text的信息,请参见Austin Group Issue #529

答案 1 :(得分:3)

对于这个问题没有实际的解决方案,因为POSIX根本没有解决这个问题。

  

不重试关闭可能会导致无法打开的文件描述符堆积起来。

尽管听起来像是合理的关注,但我从未见过这种情况,因为close()来电失败。

  

一个干净的解决方案可能涉及在新近关闭的文件描述符和一个非常复杂的锁定机制上调用fstat()

不是真的。 close()失败时,文件描述符的状态为未指定。因此,您无法可靠地使用fstat()电话。 因为文件描述符可能已经关闭了。在这种情况下,您将无效的文件描述符传递给fstat()。或其他 线程可能已经重用了它。在这种情况下,您将错误的文件描述符传递给fstat()。或者文件描述符可能已经存在 因失败的close()电话而被破坏。

当进程退出时,无论如何都将刷新并关闭所有打开的描述符。所以,这不是一个实际问题。有人可能会说,在close()经常失败的长期运行过程中,这将是一个问题。但我已经看到这种情况发生在我的经验中,POSIX也没有提供任何替代方案。

基本上,除了报告问题发生之外,你无法做很多事情。

答案 2 :(得分:2)

要缓解任何问题,请明确同步文件:

  1. (如果您在FILE*上操作,请首先在其上调用fflush()以确保用户空间缓冲区已清空到内核。)
  2. 在文件描述符上调用fsync(),将有关该文件的任何内核数据和元数据刷新到磁盘。
  3. 这些你可以在没有额外担忧的情况下重试错误。在那之后,在一些操作系统上可能泄漏文件描述符或句柄中断关闭可能是一个小问题,特别是如果你检查对你很重要的操作系统的行为(我怀疑在大多数相关操作系统中没有问题)。

    此外,一旦文件和数据被刷新,关闭期间被中断的可能性要小得多,因为关闭不应该实际接触磁盘。如果你确实得到EIO或EINTR,只需(可选)记录并忽略它,因为做其他事情可能弊大于利。这不是一个完美的世界。