程序终止时保证文件删除(C / C ++)
Win32的CreateFile
有FILE_FLAG_DELETE_ON_CLOSE
,但我在Linux上。
我想打开一个临时文件,在程序终止时将永远删除该文件。 我可以理解,在程序崩溃的情况下,保证这可能是不切实际的,但在任何其他情况下我都希望它能够工作。
我知道RAII。 我知道信号。 我知道atexit(3)
。 我知道我可以打开文件并立即删除它,文件将保持可访问状态,直到文件描述符关闭(甚至处理崩溃)。 这些似乎都不是一个完整而直接的解决方案:
- RAII:去过那里,完成了:我有一个析构函数删除文件的对象,但如果程序被一个信号终止,则不会调用析构函数。
- 信号:我正在编写一个低级库,这使得注册信号处理程序变得棘手。 例如,如果应用程序本身使用信号怎么办? 我不想踩任何脚趾。 我可能会考虑巧妙地使用
sigaction(2)
来应对……但是还没有充分考虑这种可能性。 -
atexit(3)
:显然没用,因为它在exception终止期间没有被调用(例如通过信号)。 - preemptive
unlink(2)
:这是非常好的,除了我需要文件在文件系统中保持可见(否则系统更难监控/故障排除)。
你会在这做什么?
进一步说明
我在原帖中省略了一个细节,我现在意识到我应该包括在内。 在这种情况下,“文件”不是严格意义上的普通文件,而是POSIX消息队列。 我通过mq_open()
创建它。 它可以通过mq_close()
或close()
前者是我系统中后者的别名)。 它可以通过mq_unlink()
从系统中删除。 所有这些使得它类似于常规文件, 除了我无法选择文件所在的目录。 这使得当前最流行的答案(将文件放在/tmp
)变得不可行,因为“文件”是由系统在容量非常有限的虚拟文件系统中创建的。 (我已将虚拟文件系统挂载到/dev/mqueue
,遵循man mq_overview
的示例)。
这也解释了为什么我需要名称保持可见(使立即取消链接方法不可行):“文件”必须在两个或多个进程之间共享。
在进程运行时名称仍然可见的要求使得这很难实现。 你能重新审视这个要求吗?
如果没有,那么可能没有一个完美的解决方案。 我会考虑将信号处理策略与Kamil Kisiel建议的结合起来。 在安装信号处理程序之前,您可以跟踪已安装的信号处理程序。 如果默认处理程序是SIG_IGN,则通常不会安装自己的处理程序; 如果是SIG_DFL,你会记得那个; 如果它是其他东西 – 用户定义的信号处理程序 – 你会记住指针,并安装自己的。 当你的处理程序被调用时,你会做你需要做的任何事情,然后调用记住的处理程序,从而链接处理程序。 您还将安装atexit()处理程序。 您还会记录您执行此操作以及执行此操作的信号。
请注意,信号处理是一种不完美的策略; 无法捕获SIGKILL,并且不会调用atexit()处理程序,并且文件将被保留。
David Segond的建议 – 临时文件名守护程序 – 很有趣。 对于简单的过程,这就足够了; 如果请求临时文件的进程分叉并且期望子进程此后拥有该文件(并退出),那么该守护进程在检测使用它的最后一个进程何时死亡时会出现问题 – 因为它不会自动知道打开它的进程。
如果您只是制作一个临时文件,只需在/tmp
或其子目录中创建它。 然后尽力通过atexit(3)
或类似方法将其删除。 只要您使用通过mkstemp(3)
或类似名称选择的唯一名称,即使由于程序崩溃而无法删除它,您也不会冒险在后续运行或其他此类条件下再次读取它。
那时,这只是保持/tmp
清理的系统级问题。 大多数发行版在启动或关闭时擦除它,或运行常规cronjob来删除旧文件。
也许有人已经提出了这个建议,但我无法发现它,鉴于您的所有要求,我能想到的最好的方法是将文件名以某种方式传达给父进程,例如启动脚本,这将在之后清理如果没有这样做,这个过程就会消失。 这可能通常被称为监视程序,但随后添加更常见的用例以在某种程度上失败时杀死和/或重新启动进程。
如果您的父进程也死了,那么你很幸运,但是大多数脚本环境相当健壮并且很少死,除非脚本被破坏,这通常比程序更容易保持正确。
在过去,我构建了一个“临时文件管理器”来跟踪临时文件。
可以从经理请求临时文件名,并注册此名称。
一旦您不再需要临时文件名,您就会通知管理员并且文件名未注册。
收到终止信号后,所有已注册的临时文件都被销毁。
临时文件名是基于UUID的,以避免冲突。
您可以在创建文件后使用进程fork,然后等待子进程关闭,然后父进程可以取消链接文件并退出。
我刚加入stackoverflow,发现你在这里:)
如果您遇到的问题是管理mq文件并防止它们堆积,您实际上不需要保证在终止时删除文件。 如果您只是想要堆积无用的文件,那么保留期刊可能就是您所需要的。 打开mq后,在日志文件中添加一个条目,关闭它时的另一个条目,以及初始化库时,检查日志中的不一致性,并采取纠正不一致所需的任何操作。 如果您在mq_open/mq_close
时担心崩溃,您还可以在调用这些函数之前添加日记条目。
- 在dot目录下有一个临时文件的簿记目录。
- 创建临时文件时,首先在簿记目录中创建簿记文件,该目录包含要成为临时文件的路径或UUID。
- 创建该临时文件。
- 删除临时文件时,删除簿记文件。
- 程序启动时,扫描簿记目录中是否有包含临时文件路径的文件,如果找到则尝试删除它们,删除簿记文件。
- (如果任何步骤失败,请大声记录。)
我没有办法以任何方式简单地做到这一点。 这是任何生产质量计划必须经过的样板; 轻松+500线。
你真的需要名字保持可见吗?
假设您选择立即取消链接文件。 然后:
-
preemptive unlink(2):这是非常好的,除了我需要文件在文件系统中保持可见(否则系统更难监控/故障排除)。
您仍然可以在已删除的文件上进行调试,因为它仍然可以在
/proc/$pid/fd/
下看到。 只要您了解进程的pid,枚举其打开的文件应该很容易。 -
名称需要在正常操作期间保持可见,因为它们在程序之间共享。
您仍然可以通过在Unix域套接字上传递文件描述符来共享进程之间的已删除打开文件。 有关更多信息,请参阅可在不同进程之间传递文件描