阅读如何结束 C++ 代码的答案,我了解到exit
从 C++ 代码调用是不好的。但是,如果我派生了一个子进程,该子进程必须在某个地方结束并且在调用堆栈的深处如此之深,以至于将其退出代码传递给 main 是不可能的?
我找到了一些替代方法来做到这一点 - 诚然,这已经变得有点冗长,但请耐心等待:
#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;
}
选项 0
可能是最糟糕的:
./a.out 0
~DtorTest in main, origin fork_this
~DtorTest in child, origin main
~DtorTest in main, origin main
问题是,没有调用test
in的析构函数,因为std::exit只是忽略了任何具有自动存储的对象。更糟糕的是,析构函数被调用了两次。fork_option0
unique_ptr
选项1
./a.out 1
~DtorTest in main, origin fork_this
~DtorTest in main, origin main
析构函数也存在同样的问题,fork_option1
因为std::_Exit也忽略了自动存储。至少unique_ptr
析构函数只被调用一次。
选项 2
这似乎有效,析构函数被正确调用。
./a.out 2
~DtorTest in main, origin fork_this
~DtorTest in child, origin fork_option2
~DtorTest in main, origin main
选项 3
这是从主要建议返回的最接近的近似值,但是它有几个问题:
./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
尽管fork_option3
正确调用了其中的析构函数,但发生了两次双重释放。首先是unique_ptr
,其次是 中的对象fork_this
。
选项 4
./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
比选项三略好,因为双倍免费unique_ptr
已经消失了。然而,其中的对象fork_this
仍然是双重释放的。
那么退出/结束子进程的正确方法是什么?
从上面的实验看来,选项 2 效果最好。但是,我可能错过了其他问题std::_Exit
(请参阅如何结束 C++ 代码)