0

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が自動ストレージを持つオブジェクトを単純に無視するため、 testinのデストラクタが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参照) 。

4

1 に答える 1