【发布时间】:2011-01-11 20:01:53
【问题描述】:
我正在尝试使用 fork-exec 从我的 C++ 项目中生成一个新进程。我正在使用 fork-exec 来创建通往子进程的双向管道。但我担心我在分叉进程中的资源不会被正确释放,因为 exec 调用将完全接管我的进程并且不会调用任何析构函数。
我尝试通过抛出异常并在 main 末尾的 catch 块中调用 execl 来规避此问题,但此解决方案不会破坏任何单例。
有什么明智的方法可以安全地实现这一目标吗? (希望避免任何 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;
}
【问题讨论】:
-
你说的是哪些资源?大多数文件描述符在 exec() 中存在,您可以将其标记为 close-on-exec,以便内核为您关闭它们。 pubs.opengroup.org/onlinepubs/009695399/functions/exec.html
-
顺便说一句,如果在分叉的子代和父代中都调用了析构函数,它最终会调用一次构造函数(在父代中)和两次析构函数(在父代和子代中) )。
-
我相信我在这里非常接近未定义的行为,但是 Resources 类代表了几个我用来将 C 库包装到 RAII 对象中的单例类。如果 fork 真的复制了整个进程状态,那么我可能应该在调用 exec() 之前调用 RAII 析构函数。如果资源在程序外部(如数据库连接),这当然会很疯狂。但由于它们是库,我相信它们应该在父进程和子进程中发布。 [如果有帮助,我目前正在将 ncurses、nscapi 和 SDL 包装成单例]