【问题标题】:Guaranteed file deletion upon program termination (C/C++)程序终止时保证文件删除 (C/C++)
【发布时间】:2010-10-03 01:11:02
【问题描述】:

Win32 的 CreateFileFILE_FLAG_DELETE_ON_CLOSE,但我在 Linux 上。

我想打开一个临时文件,该文件将在程序终止时被删除。我可以理解,在程序崩溃的情况下,保证这一点可能不切实际,但在任何其他情况下,我希望它能够工作。

我知道 RAII。我知道信号。我知道atexit(3)。我知道我可以打开文件并立即删除它,并且文件将保持可访问状态,直到文件描述符关闭(甚至可以处理崩溃)。这些似乎都不是一个完整而直接的解决方案:

  1. RAII:去过那里,做到了:我有一个对象,其析构函数会删除文件,但如果程序被信号终止,则不会调用析构函数。
  2. 信号:我正在编写一个低级库,这使得注册信号处理程序成为一个棘手的提议。例如,如果应用程序本身使用信号怎么办?我不想踩到任何脚趾头。我可能会考虑巧妙地使用 sigaction(2) 来应对……但还没有充分考虑这种可能性。
  3. atexit(3):显然没用,因为在异常终止期间(例如通过信号)不会调用它。
  4. 抢占式unlink(2):这很好,只是我需要文件在文件系统中保持可见(否则系统更难监控/排除故障)。

你会在这里做什么?

进一步说明

我在原始帖子中省略了一个细节,现在我意识到我应该包括在内。在这种情况下,“文件”并不是严格意义上的普通文件,而是一个 POSIX 消息队列。我通过mq_open() 创建它。它可以通过mq_close()close() 关闭(前者是我系统上后者的别名)。可以通过mq_unlink() 将其从系统中删除。所有这些都使它类似于常规文件,除了我无法选择文件所在的目录。这使得当前最流行的答案(将文件放在/tmp)不可行,因为“文件”是由系统在容量非常有限的虚拟文件系统中创建的。 (我已经按照man mq_overview 中的示例将虚拟文件系统安装在/dev/mqueue 中)。

这也解释了为什么我需要名称保持可见(使立即取消链接方法不可行):“文件”必须在两个或多个进程之间共享。

【问题讨论】:

  • 第 4 项(保持名称易于访问)使其变得困难。
  • 每次遗漏一个重要的细节,答案就会出错。下次你就知道了。

标签: c++ c file termination unlink


【解决方案1】:

如果您只是制作一个临时文件,只需在/tmp 或其子目录中创建它。然后尽最大努力通过atexit(3) 或类似方式将其删除。只要您使用通过mkstemp(3) 或类似名称选择的唯一名称,即使由于程序崩溃而无法删除它,您也不会冒险在后续运行或其他此类情况下再次阅读它。

此时这只是保持/tmp 干净的系统级问题。大多数发行版会在启动或关闭时擦除它,或者运行常规 cronjob 来删除旧文件。

【讨论】:

  • 这不是你的错,但我不能使用这个解决方案。请在问题中查看我的“进一步解释”。我无法选择文件的路径,并且它所在的文件系统的大小非常有限。如果我别无选择,我可能会用 cron 来解决它——一个丑陋的解决方法。
【解决方案2】:

过去,我构建了一个“临时文件管理器”来跟踪临时文件。

有人会向经理请求一个临时文件名,并且该名称已注册。

一旦您不再需要临时文件名,您通知管理员并且文件名被取消注册。

收到终止信号后,所有已注册的临时文件都被销毁。

临时文件名基于 UUID 以避免冲突。

【讨论】:

  • 很复杂,但显然它可以工作——如果临时文件管理器有办法检测请求临时文件的进程是否仍然存在,那就更是如此。如果该进程分叉,会有点棘手;如果父母退出,情况会加倍。
  • 您可以添加一个要求,即当进程分叉时,父进程必须通知管理器子进程的 pid。这也允许其他类型的进程间通信的灵活性。用于传递 tmp 文件的文件名。
  • 大卫,你的回答听起来好像这个“经理”作为一个模块存在于同一个过程中。乔纳森的评论听起来像是一个单独的过程。我可以看到它如何作为一个单独的进程工作,当然,但你是否建议它可以在进程中?如果是这样,我认为价值不大......
  • 临时文件管理器正在处理中。这在大多数情况下都很有效,但我同意它不是完全证明。
【解决方案3】:

在进程运行时名称保持可见的要求使得这很难实现。你能重新审视这个要求吗?

如果不是,那么可能没有完美的解决方案。我会考虑将信号处理策略与 Kamil Kisiel 的建议结合起来。您可以在安装信号处理程序之前跟踪安装的信号处理程序。如果默认处理程序是 SIG_IGN,您通常不会安装自己的处理程序;如果是 SIG_DFL,你会记得的;如果它是其他东西 - 用户定义的信号处理程序 - 你会记住那个指针,并安装你自己的。当你的处理程序被调用时,你会做任何你需要做的事情,然后调用记住的处理程序,从而链接处理程序。您还将安装 atexit() 处理程序。您还将记录您这样做,以及您这样做的信号。

请注意,信号处理是一种不完美的策略; SIGKILL 无法被捕获,并且 atexit() 处理程序不会被调用,并且文件将被保留。

David Segond 的建议 - 一个临时文件名守护程序 - 很有趣。对于简单的过程,就足够了;如果请求临时文件的进程分叉并期望子进程在此后拥有该文件(并退出),那么守护进程在检测使用它的最后一个进程何时死亡时会出现问题 - 因为它不会自动知道打开它的进程。

【讨论】:

  • 我不相信我可以删除文件名可见性的要求。我在原始问题中添加了“进一步说明”,说明这不是常规文件,它位于容量非常有限的固定位置。我想我需要让文件可见以进行监控。
  • 当然,如果有其他方法可以在没有名称可见性要求的情况下实现我的目标,我可以这样做。基本上我需要一种体面的方法来防止文件堆积,因为它们消耗的资源非常有限(想象一个容量为 16MB 和 ~200KB 文件的文件系统)。
  • 哦,让我不要忘记,这些名称需要在正常操作期间保持可见,因为它们是在程序之间共享的。我不能在它们创建后立即取消它们的链接——这会使它们变得无用。我本来应该明确说明这一点的。
【解决方案4】:

你可以在创建文件后让进程fork,然后等待子进程关闭,然后父进程可以取消链接文件并退出。

【讨论】:

    【解决方案5】:

    也许有人已经提出了这个建议,但是我无法发现它,鉴于您的所有要求,我能想到的最好的办法是让文件名以某种方式传达给父进程,例如启动脚本,这将在进程死亡后清理,如果它没有这样做的话。这可能主要被称为看门狗,但随后添加了更常见的用例以在进程以某种方式失败时终止和/或重新启动进程。

    如果你的父进程也死了,那你就很不走运了,但大多数脚本环境都相当健壮,除非脚本被破坏,否则很少会死掉,这通常比程序更容易保持正确。

    【讨论】:

    • 实际上,这是一个非常好的主意——除了它是一个正在编写的低级库。库初始化例程创建文件然后 fork。孩子继续做所有真正的工作。父母只是坐在那里,等待孩子终止,然后删除文件。
    • 这在通用例程中很难证明;程序可能对流程结构有自己的要求。如果这是允许的——你对客户有足够的控制权——那么它会非常有效。
    【解决方案6】:

    我刚加入 stackoverflow,在这里找到了你 :)

    如果您的问题是管理 mq 文件并防止它们堆积,那么您实际上不需要保证在终止时删除文件。如果您只是想避免堆积无用的文件,那么您可能只需要写日记。打开 mq 后向日志文件添加一个条目,关闭时添加另一个条目,以及初始化库时,检查日志中的不一致并采取任何必要的措施来纠正不一致。如果您担心在调用 mq_open/mq_close 时会崩溃,您还可以在调用这些函数之前添加一个日志条目。

    【讨论】:

      【解决方案7】:

      真的需要名字保持可见吗?

      假设您选择立即取消链接文件。那么:

      • preemptive unlink(2):这很好,只是我需要文件在文件系统中保持可见(否则系统更难监控/故障排除)。

        您仍然可以对已删除的文件进行调试,因为它仍会在 /proc/$pid/fd/ 下可见。只要您知道进程的 pid,枚举它们打开的文件应该很容易。

      • 名称需要在正常操作期间保持可见,因为它们在程序之间共享。

        您仍然可以通过在 Unix 域套接字上传递文件描述符来在进程之间共享已删除的打开文件。请参阅Portable way to pass file descriptor between different processes 了解更多信息。

      【讨论】:

        【解决方案8】:
        • 在您的点目录下有一个临时文件的簿记目录。
        • 创建临时文件时,首先将簿记文件创建到簿记目录中,该目录包含您的临时文件的路径或 UUID。
        • 创建该临时文件。
        • 删除临时文件后,再删除簿记文件。
        • 当程序启动时,扫描簿记目录以查找包含临时文件路径的任何文件,并尝试将其删除,如果找到,则会删除簿记文件。
        • (如果任何步骤失败,请大声记录。)

        我没有找到更简单的方法。这是任何生产质量计划都必须经过的样板;轻松 +500 行。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-02-14
          • 1970-01-01
          • 1970-01-01
          • 2014-07-10
          • 1970-01-01
          相关资源
          最近更新 更多