Чтение ответа на Как завершить код 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;
}
Возможно худшее:
./a.out 0
~DtorTest in main, origin fork_this
~DtorTest in child, origin main
~DtorTest in main, origin main
Проблема в том, что деструктор test
в fork_option0
не называется, потому что станд :: Выход просто игнорирует любой объект с автоматическим хранением. Хуже того, unique_ptr
деструктор называется дважды.
./a.out 1
~DtorTest in main, origin fork_this
~DtorTest in main, origin main
Та же проблема с деструктором в fork_option1
так как станд :: _ Выход игнорирует также автоматическое хранение. По крайней мере unique_ptr
деструктор вызывается только один раз.
Кажется, это работает, деструкторы называются правильно.
./a.out 2
~DtorTest in main, origin fork_this
~DtorTest in child, origin fork_option2
~DtorTest in main, origin main
Это ближайшее приближение к возврат из основного совет, однако у него есть несколько проблем:
./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
,
./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
все еще дважды free’d.
Итак, как правильно выйти / завершить дочерний процесс?
Из приведенных выше экспериментов может показаться, что option2 работает лучше всего. Тем не менее, я мог пропустить другие проблемы с std::_Exit
(увидеть Как завершить код C ++)
Это традиционная модель 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();
}
}
Не использовать extern "C"
для системы включайте файлы. Если вам это нужно, то вы должны использовать древний компилятор, и все ставки сняты.