如何在Linux上创建临时文件,并且无论如何都会自动清理?

8

我想在Linux上创建一个临时文件,确保该文件在我的程序终止后消失,即使它被杀死或者在错误的时刻执行了硬重启操作。使用tmpfile()函数可以为我处理所有这些问题吗?


@JarrodRoberson 我会重新添加 [race-condition] 标签 - 这完全是关于 open()unlink() 之间的时间窗口。 - thejh
@JarrodRoberson: 呃,你不是在谈论死锁吗? 我对“竞争条件”的定义大致是“存在一些并行进程,并根据它们在比赛中的速度快慢,而发生不同的事情”。 让我们看看英文维基百科上的解释:“竞争条件或竞争危险是电子系统或过程中的缺陷,其输出或结果意外且严重地取决于其他事件的顺序或时间。” - thejh
“竞态条件”是指程序由于事件的意外排序而产生对同一资源的争用,导致程序无法按照预期工作。根据您的评论,我认为您特别关注的是“死锁”,这是试图解决“竞态条件”(共享资源的争用)所导致的结果。如果您的应用程序真的如此脆弱,那么在调用“unlink()”之前异常退出时调用“tmpfile()”只是其中最不必担心的问题。 - user177800
6个回答

3
根据tmpfile()手册页面:
文件将在关闭时或程序终止时自动删除。
我没有测试过,但它似乎应该做你想要的事情。
此外:
如果未设置TMPDIR,则默认位置为/tmp。
然后,当重新启动时,/tmp将为空。

1
请查看glibc/stdio-common/tmpfile.c。据我所知,它首先创建一个文件,然后删除它。 - thejh
问题在于当您的程序异常退出时,例如出现段错误。据我所知,在exit中的任何清理都不会发生。 - daurnimator
如果您查看@thejh提到的代码,您会发现文件在打开后立即被“删除”(取消链接)。在UNIX中,只有当指向该文件的所有文件描述符都关闭时,文件才真正被删除。当进程终止时,所有文件描述符都会自动关闭(据我所知,原因并不重要)。因此,在进程退出之前不需要执行任何清理操作。 - Ivaylo Petrov

3
如果您不想使用tmpfile(),则可以在创建文件后立即使用unlink()。它将保持打开状态并分配空间,直到被关闭。
但是,在硬重启时,可能需要进行fsck以恢复空间。但由于这始终是情况,因此这种方法没有特殊的缺点。

所以即使采用这种方法,如果在open()unlink()之间发生了不太可能的情况,我的程序崩溃了,那么就会有一个空的、无用的文件,对吧?嗯,我想这比每次程序崩溃时都有一个大而无用的文件要好得多... - thejh

3

您似乎关注的是文件可能因为一些竞态条件而被留下,但我看不到这是一个问题的解释。

"竞态条件指当程序出现意外事件顺序导致争用相同资源时,程序无法按照预期工作的情况。"

我假设您在其他答案中的评论中特别关注的是死锁,这是试图更正竞态条件(共享资源争用)的结果。如果您的应用程序真的如此脆弱,调用tmpfile()并在该函数得到调用unlink()之前出现异常终止,那么这已经是您最小的担忧了。

鉴于没有提到并发、线程或其他进程共享此临时文件的文件描述符,我仍然看不到竞态条件的可能性,也许只有逻辑上的不完整的事务概念,但是它可以被检测和清理。

确保清除任何分配的文件系统资源的正确方法不仅仅是在应用程序退出时,也包括在启动时。我的所有服务器代码都会在启动之前确保上一次运行的一切都被清理干净,然后才会变得可用。

将您的临时文件放在/tmp的子目录中,并确保您的应用程序在启动和正常关闭时都会清理这个子目录。您可以使用一个shell脚本来包装您的应用程序启动,该脚本可以基于PID存在检测异常(kill -9)关闭,并执行清理活动。


2
如果我的程序可能有多个实例,那么在启动时进行清理并不容易,对吧?此外,在我看来,你可以称之为一个“竞赛”,即杀手进程和临时文件创建之间的竞争。 - thejh
"multiple instances" 是一种并发形式,在你的问题中没有提到。如果它是一个问题,你需要编辑你的问题并提供所有相关的问题,现在根据你对所有答案的扩展评论,问题的措辞不完整。 - user177800
我可以添加“多个实例”,是的。但我也可以添加“不是系统上唯一的程序”、“不一定以root身份运行”等等——这些事情难道不都很正常吗? - thejh
没有人能够读懂你的想法,你所做的假设和你认为的“正常假设”都是主观的。我的假设是应用程序创建的临时文件只适用于该应用程序实例,并且是私有实现细节,其他任何东西都是泄漏的抽象和设计缺陷。 - user177800

0
在Linux中,mktemp命令可以使用。

我假设原问题的上下文是在C/C++代码中,tmpfile(3)是c库中的一个函数。mktemp是由GNU coreutils提供的我的操作系统上的一个独立程序。此外,mktemp不会自动清理临时文件。 - Adam Monsen

0

编辑:

我检查了tmpfile源代码,它确实使用了glglgl技巧,并立即解锁了文件。

原文:

我会说不。被杀死应该可以工作,但我会假设,在硬重启之后(例如由于断电),文件仍然存在。但这取决于您的Linux发行版和使用的设置。

如果临时文件是在ramdisk中创建的,则已经消失了(有一些Unix发行版使用基于RAM的tmpfs用于临时文件)。

或者,如果您使用具有某些关于tmp的策略的环境,它也可能已经消失(可能不是即时的,但通常有策略,例如删除一个月内未访问的/tmp中的所有文件),但它也可能在标准文件系统上,其中不强制执行此类规则。在这种情况下,文件将保留。


“瞬间”?是指“没有竞态条件”还是指“很小的时间窗口”?我会说是“很小的时间窗口”… - thejh
“instantly” 意味着在调用 tmpfile 时。删除操作不是通过 atexit(或类似方式)在程序结束时进行的。但它并不是“即时”的,就像“没有竞争条件”一样。存在一个小的时间窗口,在这个窗口中发出了打开命令,但 unlink 命令还没有执行。这就是为什么我在帖子中保留了我的原始评论,因为它提供了解决竞争问题的解决方案(如 RAM 磁盘或 /tmp 策略)。 - flolo
好的,清理 /tmp 看起来像是一个可行的解决方法...虽然在服务器上,仅在启动时进行清理可能不是一个好主意。我想一个 cronjob 或者其他方式应该可以做到对程序创建的文件进行清理,只需删除所有未打开的文件即可...但这仍然感觉不太好。 - thejh
@thejh:大多数发行版已经有这样的系统了。据我所知,Ubuntu在启动时会进行检查,RedHat/Fedora/CentOS则是每天通过cronjob进行检查。您应该检查一下您使用的是哪个发行版以及它的设置情况。 - flolo

0
通常的做法是设置一个信号处理程序来清理如果程序被中断。这将无法处理kill -9或物理重启,因为它们无法被捕获。在/tmp中创建临时文件,这通常在系统启动时被清除。剩下的就是教导人们不要在不需要的情况下使用kill -9,但这似乎是一场艰苦的战斗。

嗯...有时你只需要 kill -9 某个东西。好吧,我想你对于 /tmp 是正确的 - 我应该定期清理它(看到那个文件夹里还有2011年的文件了)。然而,我不喜欢没有干净的方法来做这件事的想法... - thejh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接