我有一个必须创建临时文件的 Linux 动态库。这些文件必须有一个文件名——它们不能被创建并立即取消链接。我也无法拦截像 SIGINT 和 SIGKILL 这样的信号,因为这是一个被其他程序使用的库。
当创建文件的进程被杀死时,是否有一种理智的方法可以自动删除文件?
澄清:
这些确实是我的限制。当我刚刚在问题中说我不能 unlink() 时,请不要回答说"你可以 unlink()"。
我意识到这将需要操作系统支持——显然,当我的程序被杀死时,它本身就不能运行任何代码来删除文件。但是可能有一些方法可以标记文件,以便操作系统删除它们。
例如,Windows 有一个"关闭时删除"选项,这意味着当您的程序被杀死时,它所拥有的任何打开的文件都会被关闭并自动删除(我认为;我没有尝试过)。如果在某个地方存在这样的功能,那么理论上在 Linux 上这样的事情显然是可能的。我只想知道有没有。
- 请显示一些代码......作为一个最小的可重现示例
-
这个问题不需要代码。
-
那是你的意见。
-
为什么他们必须保留文件名?
-
它们被传递给 LLVM,它只接受文件名形式的输入(有朝一日我们有望解决这个问题,但这是一项艰巨的任务)。
-
LLVM 应在问题中提及(不在评??论中)
-
@BasileStarynkevitch,诸如"为什么这段代码不起作用?"之类的问题必须提供 MCVE。这个问题不符合这个模式。 LLVM 无关紧要,因为与任何只接受文件名的程序的交互都会遇到同样的问题。
-
LLVM 可以从标准输入读取它,不是吗?
-
LLVM 是相关的,因为它接受文件名。
-
LLVM 无法从标准输入中读取,LLVM 本身与问题无关。我不会为此重新架构 LLVM。我已经清楚地解释了问题中的限制。
-
文件必须有你说的名字?取消链接后尝试 /proc/<pid>/fd/<fd> 。但不能保证在所有情况下都能正常工作。
-
您还可以让库分叉一个看门狗进程。或者,如果您真的很偏执,两个监视父进程和彼此的看门狗进程。
-
@n.m.:你的意思是之前,不是之后,对吧?
-
@LightnessRacesinOrbit 如果您愿意,您可以在之前执行此操作,但这没有多大意义,因为在取消链接之前,文件仍然可以以其原始名称使用,您可以使用它来代替。
-
@n.m。呃,没关系,我在脑海中混淆了"使用 /proc/.. 路径"和"获取文件的句柄":D
创建一个文件,然后将 /proc/self/fd/X 传递给 LLVM,其中 X 是您的文件描述符。您现在可以取消链接(正如 Basile 建议的那样),
由于 /proc/self 仅在您的程序关闭时才会消失,因此名称和文件的寿命足够长。
-
我认为应该在取消链接之前打开 /proc/self/fd/X
-
这几乎可以工作(出于我的目的),但不幸的是,我的程序有很多临时文件(如 5000),而 ulimit -n 在大多数系统上默认为 1024。愚蠢的Linux。可能不得不接受我的回答。 :-/
-
取消链接文件时无关紧要,您可以在启动子进程之前或之后进行。重要的是,只要子进程可能想要使用 /proc/<PID>/fd/<FD> 路径,您的进程就会保持文件打开,因为关闭它会删除该目录条目,AFAIK。顺便说一句:真的可以使用 self 作为路径中的组件吗?一个进程自身与另一个进程不同。
-
@UlrichEckhardt:这个问题专门讨论了一个在进程中执行的库。所以是的,在这种情况下 /proc/self 有效。
-
真的。我的印象是 LLVM 是由图书馆在一个单独的进程中启动的,但鉴于现有的信息,情况并非如此。
虽然这不是特别明智,但 Linux 允许您通过 /proc/$pid/fd/$number 传递已删除文件的名称。
-
第一个实际上并没有忽略问题的答案,谢谢!
-
如果您通过此路径打开了文件的句柄,则不会删除这些文件。当不再存在指向它们的链接时,文件将被删除(包括这个!)
另一个可能的解决方案是 fork() 一个子进程。然后通过某种机制将所有临时文件名发送到此进程。
子进程可以像这样注册以知道其父进程何时被杀死:
1 2 3 4 5
| #include <sys/prctl.h>
int ret;
ret = prctl (PR_SET_PDEATHSIG, SIGUSR1);
if (ret)
perror ("prctl"); |
然后它会在父级被杀死时收到SIGUSR1。此时就可以正常删除文件了。
不,在使用您的库的程序没有运行后,您不能删除这些,因为使用它的程序不再运行。
相反,您可能应该
-
在库的正常操作中检查过时/过大/剩余的临时文件(下次运行时清理)
-
创建一个单独的程序来为您管理这些(定期清理)
这感觉像是一个相当普遍的问题(我当然有过),但它源于对您的程序可能发生的事情的误解。 SIGKILL 将在您的程序可以处理的范围之外立即终止它,并且不会运行它的进一步操作。 (一些特殊的可以继续运行)
正如这篇关于这个主题的帖子所说的那样
SIGKILL pulls the rug out from your running process, terminating it immediately.
-
请注意,这并不明显。在 Windows 上,您可以在程序停止运行时删除文件,甚至可以从库中删除。它的 Linux 缺少这个特性。
-
也许在正常终止期间,但总有您无法控制的状态,例如无法以这种方式处理的断电。
-
很有可能它仍然可以工作。 Windows 更喜欢将这些文件保存在缓存中,因为它知道它们不是持久的。 NTFS 事务日志是第二道防线。
I have a Linux dynamic library that has to create temporary files.
您可以使用 atexit(3) 注册一个处理程序,该处理程序将在 exit(3) 时间(或 main 的正常终止)删除所有这些临时文件。当然,这不适用于信号。
您可以在一些 tmpfs 文件系统中创建这些文件。然后它们将在关机时被删除。
如果您遵循有关这些文件的一些命名约定,您还可以使用一些清理脚本(由一些 crontab 条目触发)发布您的库。
Is there a sane way to have the files automatically deleted when the process that created them is killed?
一般来说不是(并且不可能有一个具有 POSIX 文件语义的)。您可能会编写一个清理程序(可能使用 inotify(7) 工具)以从外部运行(例如,作为 crontab 作业或某个守护进程)。
您还可以在创建后(使用 open 或 creat)取消链接(2)每个此类临时文件,并为其保留文件描述符。然后,当进程终止时,或者当它 close-s 那个文件描述符时,文件资源被回收。 tmpfile(3) 使用了这个技巧。
顺便说一句,如果你使用 LLVM 作为 JIT 翻译器,你可以考虑使用 libgccjit。它能够在没有任何输入文件的情况下生成代码。
这样的临时文件不能被自动删除,因为其他一些进程可以打开它们(通过它们的名字)——在任意时刻。这就是为什么 Linux 不能"关闭时删除"(相反,据传 Windows 只允许单个进程写入给定文件)。
But there may be some way to mark the files so that the OS deletes them.
不,不在 Linux 或 POSIX 上。该功能应由应用程序代码提供。
- 是的,可悲的是,这只适用于正常的程序终止,除了 RAII 对我没有任何帮助。
-
我不能unlink()。我说我需要保留文件名。并且向 crontab 添加清理脚本对于库来说并不是一个明智的解决方案。
-
那你有什么建议?我试图解释你的期望是不合理的(你想要达到的目标是不可能的)
-
我希望得到一个答案,要么实际上回答了这个问题,要么给出了一些不可能的权威理由。如果我的约束不是真正的约束,那么我不会把它们放在问题中!