How to end C++ code への回答を読んで、 C++ コードexit
からの呼び出しが悪いことを知りました。しかし、どこかで終了する必要があり、呼び出しスタックの奥深くにある子プロセスを fork した場合、その終了コードを 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
問題は、 std::exitが自動ストレージを持つオブジェクトを単純に無視するため、 test
inのデストラクタがfork_option0
呼び出されないことです。さらに悪いことに、デストラクタが2 回呼び出されます。unique_ptr
オプション1
./a.out 1
~DtorTest in main, origin fork_this
~DtorTest in main, origin main
std::_Exitは自動ストレージも無視するfork_option1
ため、デストラクタと同じ問題があります。少なくとも、デストラクタは 1 回だけ呼び出されます。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
は正しく呼び出されますが、2 つの二重解放が発生します。最初はunique_ptr
、2 番目は のオブジェクトです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
のダブルフリーがなくなったため、オプション 3 よりもわずかに優れています。ただし、オブジェクトfork_this
はまだ二重に解放されています。
では、子プロセスを終了/終了する適切な方法は何ですか?
上記の実験から、オプション 2 が最適に機能するように思われます。ただし、他の問題を見逃している可能性があります( C++ コードの終了方法をstd::_Exit
参照) 。