1

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

但是,我不想在主函数中处理临时目录的创建和删除,因为我认为只有实际使用该目录的函数才应该负责它的创建和删除。并且 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 ...")
  }
};
4

4 回答 4

7

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

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

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

于 2009-01-25T17:54:48.563 回答
2

对于 Unix:

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

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

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

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

是的,信号很复杂。

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

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

这就是我说出来的结果。

于 2009-01-25T16:52:50.687 回答
0

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

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

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

于 2009-01-25T16:31:09.653 回答
0

几个想法:

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

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


补充:另见:

于 2009-01-25T19:39:40.537 回答