21

Win32CreateFileFILE_FLAG_DELETE_ON_CLOSE,但我在 Linux 上。

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

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

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

你会在这里做什么?

进一步说明

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

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

4

8 回答 8

7

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

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

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

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

于 2009-01-23T02:52:42.793 回答
6

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

那时,它只是保持/tmp清洁的系统级问题。大多数发行版在启动或关闭时擦除它,或运行常规 cronjob 以删除旧文件。

于 2009-01-23T00:00:41.690 回答
4

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

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

于 2009-01-23T14:21:01.033 回答
3

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

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

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

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

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

于 2009-01-23T00:02:42.763 回答
1

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

于 2009-01-23T14:19:19.337 回答
1

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

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

于 2009-04-28T16:45:23.107 回答
1

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

假设您选择立即取消链接文件。然后:

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

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

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

    您仍然可以通过在 Unix 域套接字上传递文件描述符来在进程之间共享已删除的打开文件。有关更多信息,请参阅在不同进程之间传递文件描述符的可移植方式

于 2012-08-14T00:35:40.837 回答
1
  • 在您的点目录下有一个临时文件的簿记目录。
  • 在创建临时文件时,首先在包含路径或 UUID 到您的临时文件的簿记目录中创建簿记文件。
  • 创建该临时文件。
  • 删除临时文件后,再删除簿记文件。
  • 当程序启动时,扫描簿记目录以查找包含临时文件路径的任何文件,如果找到,则尝试删除它们,它们会删除簿记文件。
  • (如果任何步骤失败,请大声记录。)

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

于 2015-08-05T06:48:11.077 回答