我试图从我的C ++项目使用fork-exec产生一个新的进程。 我正在使用fork-exec来为subprocess创build一个双向pipe道。 但是我担心分叉进程中的资源不会被正确释放,因为exec-call将完全接pipe我的进程,不会调用任何析构函数。
我试图通过抛出一个exception,并在主结束时从catch块调用execl来绕过这个问题,但是这个解决scheme不会破坏任何单例。
有没有什么明智的方法来安全地实现这一点? (希望避免任何atExit黑客)
例如:以下代码输出:
We are the child, gogo! Parent proc, do nothing Destroying object
即使分叉进程也有一个单独的副本,需要在我调用execl之前被破坏。
#include <iostream> #include <unistd.h> using namespace std; class Resources { public: ~Resources() { cout<<"Destroying object\n"; } }; Resources& getRes() { static Resources r1; return r1; } void makeChild(const string &command) { int pid = fork(); switch(pid) { case -1: cout<<"Big error! Wtf!\n"; return; case 0: cout<<"Parent proc, do nothing\n"; return; } cout<<"We are the child, gogo!\n"; throw command; } int main(int argc, char* argv[]) { try { Resources& ref = getRes(); makeChild("child"); } catch(const string &command) { execl(command.c_str(), ""); } return 0; }
有很好的可能性,你不需要在fork
和exec
之间调用任何析构函数。 是的, fork
创建了整个进程状态的一个副本,包括具有析构函数的对象,并且exec
删除了所有状态。 但是这实际上很重要吗? 你的程序之外的观察者是否可以在同一台计算机上运行另一个不相关的进程 – 告诉解析器没有运行在孩子身上? 如果没有办法告诉,就没有必要运行它们。
即使外部观察者可以看出,在孩子中运行析构函数可能是主动的错误 。 通常的例子是:假设你在调用fork
之前写了一些stdout
函数,但是它被缓冲到了库中,所以还没有被传送到操作系统。 在这种情况下,你不能调用孩子的stdout
fclose
或fflush
,否则输出将会发生两次! (这也是为什么你几乎肯定应该调用_exit
而不是exit
如果exec
失败。)
说了这么多,有两种常见的情况,你可能需要在孩子身上做一些清理工作。 一个是文件描述符( 不要将这些与stdio FILE或iostream对象混淆),在exec
之后不应该打开。 处理这些问题的正确方法是在打开FD_CLOEXEC
标志后尽快设置FD_CLOEXEC
标志(有些OS允许你自己open
,但这不是通用的)和/或从3循环到一些大的号码在小孩中close
( 而不是 fclose
)。 (FreeBSD已经closefrom
,但据我所知,没有其他人这样做,这太遗憾了,因为它非常方便。)
另一种情况是系统全局线程锁 – 这是一个棘手的,标准化程度不高的领域 – 可能会被父母和孩子所掌控,然后跨越exec
继承到一个不知道它锁定的进程中。 这是pthread_atfork
应该是为什么,但我已经读过,在实践中,它不能可靠地工作。 我可以提供的唯一建议是:“当你打电话给fork
时候不要拿着任何锁。”对不起。