【问题标题】:How can I delete a file after another process finishes with it?在另一个进程完成后如何删除文件?
【发布时间】:2013-12-03 14:08:13
【问题描述】:

我想打开内存中的数据。我在 ubuntu 上创建了一个临时文件并 fork xdg-open 以使用标准应用程序打开一个文件。我想在程序关闭后删除临时文件。问题是xdg-open 本身会打开另一个进程并立即退出。因此,我不知道是什么进程打开了文件。我尝试使用flock 获得排他锁,并在等待20 srcs 后尝试unlink 文件(以确保其他进程首先打开文件),调用成功并且打开的程序简单地关闭文件。我想等到程序关闭后再删除文件。有谁知道如何做到这一点?

【问题讨论】:

  • 也许创建一个冗余的文件指针数组,fork 在打开文件时会更新到该数组?
  • 原则上您可以立即删除(取消链接)该文件。从逻辑的角度来看,这将“删除”文件,但只要其他程序打开物理存储,它就会一直存在。其他进程尝试做的任何事情仍然有效。当另一个进程关闭文件并退出时,它什么都没有了。
  • @Damon 是的,正如我提到的,我尝试过,但它会导致应用程序关闭文件。
  • 你是如何运行 xdg-open 的? systemexec?
  • @Duck fork -> execvp

标签: c++ c linux ubuntu


【解决方案1】:

当xdg-open不依赖后缀时,可以使用

fd = open(filename, O_RDONLY|O_CLOEXEC);
unlink(filename);

system("xdg-open /proc/%u/fd/%u", getpid(), fd); /* pseudo-code! system is not printf() like! */

close(fd);

【讨论】:

  • 如果xdg-open(或它调用来打开文件的二进制文件)在子进程中分叉执行实际应用程序,并自行退出,则system()调用可以返回,并且描述符关闭(因此消失)在实际应用程序有机会尝试打开它之前。取决于xdg-open 的实现方式——它通常是一个执行依赖于桌面环境的二进制文件的脚本——这可能有效,也可能无效。
【解决方案2】:

您可以使用 inotify (see man 7 inotify) 或文件租约 (see man 2 fcntl, Leases section) 来检测其他进程何时打开和关闭文件(使用 inotify),或者确定文件是否被其他进程打开(文件租约)。

核心问题是xdg-open 通常是一个脚本,它检查环境(可能还有目标文件),然后执行一个二进制文件(反过来可能检查目标文件并执行另一个二进制文件),并且有可能这里的一个或多个阶段分叉并立即退出,客户端进程继续执行链。

这意味着system()返回的时间点基本上是无关紧要的。目标文件此时可能已被最终应用程序打开,也可能未打开;我们只是不知道,也无法知道。

一种选择是创建一个单独的进程组 (session),并监视会话,只要进程组存在就保留原始文件。但是,这假设xdg-open 链中的任何脚本或二进制文件都没有创建自己的会话。 (我不知道他们是否这样做,有很多不同的实现——每个桌面环境都使用自己的二进制文件,xdg-open 是围绕它们的兼容性包装器。)

实际上,这意味着将system() 替换为您自己的实现,使用fork()setsid()exec*()waitpid()waitid();循环中的最后一个,短暂睡眠以检测进程组中何时没有更多进程。

另一种选择是执行命令,然后(派生一个子进程)等待一段特定的时间——也就是说,只要普通用户可以容忍等待文件开始加载;几秒钟,换句话说——,然后开始检查文件是否仍在使用中。文件不再使用后,可以取消链接。

使用inotify(),您可以在执行xdg-open 命令之前分配监视,然后监视打开和关闭。因为xdg-open 可能会检查目标文件以选择应用程序,所以您不能假设第一次关闭是最终关闭;您还需要等待上面提到的特定时间,以确保应用程序开放链已经完成。 那么,如果关闭次数与打开次数一样多,则可以取消链接文件。否则,您将等待剩余的关闭,并在最后一个关闭后取消链接。

使用file leases,方法稍微简单一些,但也比较局限。您只能获得用户自己拥有的普通文件的文件租约。只有当文件没有被任何进程(包括同一进程的其他描述符)打开以进行写入时,您才能获得读取租约。只有当文件根本没有被任何进程打开(包括同一进程的其他文件描述符)时,您才能获得写租约。当您持有租约时,任何其他打开文件的进程(如果您持有写租约)或尝试修改它(如果您持有读或写租约)将导致SIGIO 信号(默认情况下,您可以将其更改为实时信号)发送给租赁持有人。它最多有/proc/sys/fs/lease-break-time秒来降级或释放租约,直到内核强行破坏它;在此期间,打开器/文件修饰符将被 open()/truncate() 调用阻止。

在执行xdg-open 之前,您可以尝试获取文件的写租约。如果成功,您就知道这是它唯一打开的文件描述符。调用xdg-open 后,当文件打开(或由其中一个二进制文件检查)时,租约将被破坏;您可以在通话前简单地释放租约以避免麻烦。 从执行xdg-open 开始经过适当的秒数(人类等待应用程序开始打开文件的时间)后,您开始定期检查文件是否仍被其他进程打开通过尝试获得写租约。如果写入租约成功,并且从您启动 xdg-open 开始已经过去了足够的时间,那么您知道“人类用户”会变得太沮丧而无法再等待打开文件,或者应用程序已经已关闭文件,因此可以取消链接。

以上所有因素都可以结合起来,让你随心所欲,但就我个人而言,我相信建模人类行为的方法是最稳健的。我个人会让时间限制(和写入租约尝试间隔)很容易配置,分别有 10 秒和 1 秒的默认值。

最后,如果资源使用是一个问题,那么我建议编写一个单独的辅助二进制文件来为您管理它。基本上,不是运行xdg-open [OPTIONS] FILENAME,而是运行/usr/lib/myapp/open DELAY INTERVAL [OPTIONS] FILENAME/usr/lib/myapp/open 二进制分叉并立即退出。子进程既执行xdg-open,又执行上述过程以等待文件可以被取消链接。每个/usr/lib/myapp/open 二进制文件都需要很少的数据(最小的驻留集大小)和资源(它们大多处于休眠状态),因此即使在内存中拥有几十个它们也不会显着消耗即使在嵌入式 Linux 机器上。

如果有兴趣,我可以在此处添加 /usr/lib/myapp/open 的示例 C 实现。 (让我知道这三种方法中哪一种最有趣——进程组监控、inotify 或文件租约。)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-27
    • 1970-01-01
    • 2012-10-27
    • 2011-08-09
    相关资源
    最近更新 更多