【问题标题】:Create temp dir that is globally known and that gets automagically removed (C++)?创建全局已知并自动删除的临时目录(C++)?
【发布时间】:2009-01-25 16:19:27
【问题描述】:

在 C++ 中,我有一些函数需要写入临时目录。理想情况下,只创建一个它们都写入的临时目录(以最小化 I/O 开销)。该目录应在程序退出时自动删除。

但是,我不想在 main 函数中处理临时目录的创建和删除,因为我认为只有实际使用该目录的函数应该负责它的创建和删除。并且 main 函数并不一定知道使用了一个临时目录。

这是我尝试过的(参见下面的代码):可以从任何地方全局调用的 getTempDir() 函数仅在第一次调用时创建一个目录,并在每次调用时返回目录名称。在第一次调用时,它还会为一个小 DirRemover 对象创建一个静态 boost::shared_ptr,该对象的析构函数将删除目录。程序退出时会自动调用该析构函数。

问题是它不会在程序不成功退出或杀死等时调用 FileRemover 析构函数。有更好的解决方案吗?

代码如下:

std::string getTempDir(){
  static bool alreadyThere = false;
  static std::string name = "";
  if(!alreadyThere){
    // create dir with
    // popen("mktemp -p /tmp","r")) 
    // and store its name in 'name' 
    removeAtEnd(name);
    alreadyThere = true;
  }
  return name;
}

void removeAtEnd(const std::string& name){
  static boost::shared_ptr<DirRemover> remover(new DirRemover(name));
}

struct DirRemover {
  std::string name;
  DirRemover(const std::string& n) : name(n){}
  ~DirRemover(){
    // remove 'name' dir with popen("rm -r ...")
  }
};

【问题讨论】:

    标签: c++ file-io global-variables signal-handling


    【解决方案1】:

    使用 popen() 执行诸如“rm -r”或“mktemp -p /tmp”之类的东西是灾难的根源。在我看来,这是非常糟糕的风格。

    UNIX 特定:如果您希望临时文件消失,即使您的应用程序异常终止,那么最好的方法是在打开临时文件后立即取消链接。您的应用程序仍将拥有它们的文件句柄,以便您可以使用该文件。当您的应用程序终止并关闭文件描述符时,该文件将自动从文件系统中删除。

    顺便说一句,我看到你正在使用 Boost。你确定Boost.Filesystem没有上述功能吗?

    【讨论】:

    • 好主意,取消链接。还没有考虑过(尽管我之前曾被这种行为所困;P)。
    • +1 好主意 - 这可能是我一直在寻找的在 Win32 中打开具有 FILE_FLAG_DELETE_ON_CLOSE 属性的文件的等价物。
    【解决方案2】:

    对于 Unix:

    ::signal(SIGINT, sigintHandler);
    ::signal(SIGKILL, sigkillHandler); //etc
    

    让这些处理程序将退出事件推送到您的事件队列(或等效队列)并忽略该信号。然后,您的应用程序将能够正常退出。但是,有一个信号 (SIGKILL) 不容忽视。 为了处理这个问题,确定你真正想要在杀死时释放的最关键的资源(我不相信临时目录有那么重要。只需在下次启动时删除。),然后在您的处理程序,在您的相关单例上调用 release 函数。

    详细说明单例::signal 无法调用绑定函数,因此您的处理程序只能访问单例/全局变量。如果你的设计没有用单例组织事物(它不应该),你仍然可以经常破解它。例如,如果您在堆栈中有多个 MainWindows,那么您还可以拥有一个指向所有它们的全局指针向量,如果需要,您可以从处理程序中访问。

    请注意,对于常见的杀戮(例如kill foo),您会得到 SIGQUIT 而不是 SIGKILL - 并且 SIGQUIT 可以忽略不计。 SIGKILL 仅适用于kill -9 foo(在这种情况下,用户可能甚至想要你进行清理)。

    是的,信号很复杂。

    edit -我被告知 SIGKILL 不仅不可忽略,而且无法捕获。的确,在 SIGKILL 上你可以做的事情并不多。删除临时目录?为什么?那个临时目录的存在是有原因的。

    假装 sigkill 是断电并在下次启动时恢复 - 除非您可以按照其他人评论的关于取消链接的特定 hack 进行。

    这就是我说出来的结果。

    【讨论】:

    • +1 表示将“单身人士”这个词扔出去。是的,UNIX 信号是处理这个问题的好方法。
    【解决方案3】:

    如果您的进程被杀死(例如,从任务管理器中),则无法进行任何清理。

    如果您的程序停止,其他进程应该执行该清理。启动该进程并将您的程序作为其子进程启动。此过程在启动程序之前创建临时目录,并在程序完成后将其删除。

    虽然如果你的包装进程被杀死,这仍然不能解决问题。

    【讨论】:

    • 正如其他人所指出的,这取决于进程被杀死的确切机制。通常应该在 SIGKILL 之前尝试 SIGTERM,前者让应用程序可以选择捕获信号并优雅地处理它......
    【解决方案4】:

    一些想法:

    • 保持最小耦合:为什么不创建一个服务类,它位于您的其他代码与内容写入位置和删除时间的详细信息之间?使用通常的 singleton 模式 方法来确保它只被初始化一次。

    • 确保删除:信号已经被提及作为一种允许您在 SIGFPE、SIGSEGV、SIGTERM 等事件中优雅地清理的方法......这些对SIGSTOP 或 SIGKILL 事件。但是,对于在磁盘上创建文件的文件,有一个旧的 unix 技巧,然后是 unlink 它——此时文件仍在使用中并且实际上并没有被删除,但它会在你编程时使用放弃它的文件描述符——你会得到自动删除。我不知道这将如何与目录一起使用,但您当然可以使其与其中包含的所有文件一起使用。作为奖励(或成本,取决于),在调用 unlink 之后,这些文件对其他进程不可见或无法访问。


    添加:另见:

    【讨论】:

      猜你喜欢
      • 2019-01-06
      • 1970-01-01
      • 2010-10-10
      • 2011-12-17
      • 2013-02-08
      • 1970-01-01
      • 2010-12-07
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多