【问题标题】:POSIX/UNIX: How to reliably close a file descriptorPOSIX/UNIX:如何可靠地关闭文件描述符
【发布时间】:2017-03-08 12:59:44
【问题描述】:

问题:

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

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

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

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


编辑:

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

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

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

【问题讨论】:

标签: c++ multithreading unix freebsd


【解决方案1】:

此问题已在 POSIX 中修复,以供下一个问题;不幸的是,它的变化太大而无法进入最近的 TC2。 Austin Group Issue #529the final accepted text

【讨论】:

    【解决方案2】:

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

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

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

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

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

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

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

    【讨论】:

    • 我能想到的唯一可能的解决方案是调用fstat()之前调用close(),如果close()调用失败,调用fstat()再次尝试确定它是否与描述符另一端的文件/对象相同。但这只有在您知道另一个线程无法打开同一个文件/对象时才有效。正如您已经注意到的,这不是一个大问题 - 我从未见过 close() 调用失败。
    【解决方案3】:

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

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

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

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

    【讨论】:

    • “刷新文件”是什么意思?这是文件描述符 I/O,而不是文件流 I/O;没有要刷新的用户级缓冲区。
    • @JonathanLeffler 编辑答案,更好吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-02-14
    • 2011-10-30
    • 1970-01-01
    相关资源
    最近更新 更多