[英]Correctly exit forked process in C++
Reading the answer to How to end C++ code , I learned that calling exit
from C++ code is bad. 阅读有关如何结束C ++代码的答案,我了解到从C ++代码调用
exit
是不好的。 But what if I have forked a child process which must end somewhere and is so deep down the call stack that passing down its exit code to main would be impossible? 但是,如果我派生了一个子进程,该子进程必须终止于某个地方并且在调用堆栈中非常深,以至于无法将其退出代码传递给main怎么办?
I found a few alternatives how to do that -- admittedly, this has become a bit lengthy, but bear with me: 我找到了一些替代方法,虽然这样做有些冗长,但请耐心等待:
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdlib>
#include <memory>
#include <string>
#include <iostream>
thread_local char const* thread_id{"main"};
struct DtorTest {
std::string where{};
DtorTest(void) = default;
DtorTest(std::string _where) : where{std::move(_where)} {}
~DtorTest(void) {
std::cerr << __func__ << " in " << thread_id << ", origin " << where << std::endl;
}
};
std::unique_ptr< DtorTest > freeatexit{nullptr};
pid_t
fork_this(pid_t (*fork_option)(void))
{
DtorTest test(__func__);
return fork_option();
}
pid_t
fork_option0(void)
{
pid_t pid;
if ((pid = fork()))
return pid;
thread_id = "child";
DtorTest test(__func__);
std::exit(EXIT_SUCCESS);
}
pid_t
fork_option1(void)
{
pid_t pid;
if ((pid = fork()))
return pid;
thread_id = "child";
DtorTest test(__func__);
std::_Exit(EXIT_SUCCESS);
}
pid_t
fork_option2(void)
{
pid_t pid;
if ((pid = fork()))
return pid;
{
thread_id = "child";
DtorTest test(__func__);
}
std::_Exit(EXIT_SUCCESS);
}
pid_t
fork_option3(void)
{
pid_t pid;
if ((pid = fork()))
return pid;
thread_id = "child";
DtorTest test(__func__);
throw std::exception();
}
int main(int argc, char const *argv[])
{
int status;
const int option = (argc > 1) ? std::stoi(argv[1]) : 0;
pid_t pid;
freeatexit = std::unique_ptr< DtorTest >(new DtorTest(__func__));
switch (option) {
case 0:
pid = fork_this(fork_option0);
break;
case 1:
pid = fork_this(fork_option1);
break;
case 2:
pid = fork_this(fork_option2);
break;
case 3:
try {
pid = fork_this(fork_option3);
} catch (std::exception) {
return EXIT_SUCCESS;
}
break;
case 4:
try {
pid = fork_this(fork_option3);
} catch (std::exception) {
std::_Exit(EXIT_SUCCESS);
}
break;
default:
return EXIT_FAILURE;
}
waitpid(pid, &status, 0);
return status;
}
Possibly the worst: 可能是最坏的情况:
./a.out 0
~DtorTest in main, origin fork_this
~DtorTest in child, origin main
~DtorTest in main, origin main
The problem is, that the destructor of test
in fork_option0
is not called, because std::exit simply ignores any object with automatic storage. 问题是,没有调用
fork_option0
的test
的析构函数,因为std :: exit只会忽略任何具有自动存储的对象。 Worse, the unique_ptr
destructor is called twice . 更糟糕的是,
unique_ptr
析构函数被调用了两次 。
./a.out 1
~DtorTest in main, origin fork_this
~DtorTest in main, origin main
Same problem with the destructor in fork_option1
because std::_Exit ignores automatic storage as well. fork_option1
的析构函数也存在同样的问题,因为std :: _ Exit也会忽略自动存储。 At least the unique_ptr
destructor is called only once. 至少
unique_ptr
析构函数仅被调用一次。
This seems to work, the destructors are called correctly. 这似乎可行,析构函数被正确调用。
./a.out 2
~DtorTest in main, origin fork_this
~DtorTest in child, origin fork_option2
~DtorTest in main, origin main
This is the nearest approximation to the return from main advice, however it has several problems: 这是最接近主要建议收益的近似值,但是它有几个问题:
./a.out 3
~DtorTest in main, origin fork_this
~DtorTest in child, origin fork_option3
~DtorTest in child, origin fork_this
~DtorTest in child, origin main
~DtorTest in main, origin main
Although the destructors in fork_option3
are correctly invoked, two double frees happen. 尽管可以正确调用
fork_option3
中的析构函数, fork_option3
会发生两次双重释放。 First the unique_ptr
, second the object in fork_this
. 首先是
unique_ptr
,其次是fork_this
的对象。
./a.out 4
~DtorTest in main, origin fork_this
~DtorTest in child, origin fork_option3
~DtorTest in child, origin fork_this
~DtorTest in main, origin main
Slightly better than option three, because the double free of the unique_ptr
is gone. 比选项三略好一点,因为对
unique_ptr
的双重释放已经消失了。 However the objects in fork_this
are still double free'd. 但是
fork_this
中的对象仍然是double fork_this
。
So what is the proper way to exit/end a child process? 那么退出/结束子进程的正确方法是什么?
From the above experiments it would seem that option2 works best. 从以上实验看来,option2效果最好。 However, I might have missed other problems with
std::_Exit
(see How to end C++ code ) 但是,我可能已经错过了
std::_Exit
其他问题(请参阅如何结束C ++代码 )
This is the traditional pattern of fork
. 这是
fork
的传统图案。
#include <sys/stat.h>
#include <unistd.h>
#include <sys/wait.h>
#include <cstdlib>
#include <iostream>
#include <fstream>
struct DtorTest {
~DtorTest(void) { std::cout << "d'tor never runs\n"; }
};
int
child(void)
{
// only child
DtorTest dtortest; // D'tor never runs
std::ofstream fout("inchild.txt"); // file not flushed
fout << "this is in the child\n";
return 0;
}
int
main(void)
{
pid_t pid;
if ((pid = fork()))
int status;
waitpid(pid, &status, 0);
return status;
} else {
return child();
}
}
Don't use extern "C"
for the system include files. 不要对系统包含文件使用
extern "C"
。 If you need that, then you must be using an ancient compiler, and all bets are off. 如果需要的话,那么您必须使用古老的编译器,所有的选择都关闭了。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.