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

POSIX/UNIX: How to reliably close a file descriptor

本文关键字:文件 描述 UNIX 何可 POSIX      更新时间:2023-10-16

问题:

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

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

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

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


编辑:

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

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

可以考虑在调用close((期间阻塞信号。

此问题已在POSIX中为下一个问题修复;不幸的是,这是一个太大的变化,无法进入最近的TC2。参见Austin Group第529期的最终接受文本。

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

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

尽管这听起来是合理的担忧,但我从未见过由于close()调用失败而发生这种情况。

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

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

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

基本上,除了报告问题发生之外,你对此无能为力。

要缓解任何问题,请显式同步文件:

  1. (如果您在FILE*上操作,请首先在其上调用fflush(),以确保用户空间缓冲区已清空到内核。(
  2. 在文件描述符上调用fsync(),将有关该文件的任何内核数据和元数据刷新到磁盘

您可以在出现错误时重试,而无需额外担心。在那之后,在某些操作系统上,可能在中断关闭时泄露文件描述符或句柄可能是一个小问题,尤其是如果您检查对您来说很重要的操作系统的行为(我怀疑在大多数相关的操作系统中都没有问题(。

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