Разветвление и ожидание в Linux (C++).

Я хочу разветвить процесс, а затем сделать следующее в родительском:

  1. Подождите, пока он завершится естественным образом или истечет время ожидания, установленное родителем (что-то вроде waitforsingalobject в Windows), после чего я убью процесс, используя kill (pid);

  2. Получить код выхода дочернего процесса (при условии, что он завершился естественным образом)

  3. Мне нужно иметь доступ к std::cout дочернего процесса от родителя.

Я попытался использовать waitpid(), однако, хотя это позволяет мне получить доступ к коду возврата, я не могу реализовать тайм-аут с помощью этой функции.

Я также рассмотрел следующее решение (https://www.linuxprogrammingblog.com/code-examples/signal-waiting-sigtimedwait), что позволяет мне реализовать тайм-аут, однако, похоже, нет способа получить код возврата.

Полагаю, мой вопрос сводится к тому, как правильно добиться этого в Linux?


person SFD    schedule 23.05.2017    source источник
comment
Вы можете использовать waitpid с параметром WNOHANG для опросить статус дочернего процесса. Делайте это в цикле, читая вывод дочернего процесса до истечения времени ожидания (или дочернего процесса).   -  person Some programmer dude    schedule 23.05.2017
comment
Кроме того, если вы внимательно посмотрите на пример, на который вы ссылаетесь, он имеет вызов waitpid после завершения (или уничтожения) дочернего процесса, что позволяет вам получить код выхода.   -  person Some programmer dude    schedule 23.05.2017
comment
второй параметр waitpid — это статус. Вы можете получить код возврата из него с помощью макроса WEXITSTATUS.   -  person nefas    schedule 23.05.2017
comment
Использование waitpid с WNOHAND кажется неэффективным и, возможно, некрасивым решением. Я не совсем уверен, какова цель последнего вызова waitpid. Давайте предположим, что дочерний процесс не завершается, а затем завершается естественным образом, поэтому не будет ли процесс завершен к моменту вызова waitpid? или я что-то пропустил здесь?   -  person SFD    schedule 23.05.2017
comment
Если дочерний процесс завершается, он становится так называемым зомби и задерживается в системе (даже если ничего не запущено) до тех пор, пока вы не вызовете одну из функций wait для получения информации о состоянии процесса. Я думаю, вам нужно немного поискать и узнать больше о том, как работают процессы в системах POSIX (таких как Linux и macOS). Короче говоря, программа, на которую вы ссылаетесь, должна работать точно так, как вы хотите (в отношении времени ожидания), если вы просто получаете статус от этого вызова waitpid (вместо передачи NULL).   -  person Some programmer dude    schedule 23.05.2017
comment
Что касается получения вывода из дочернего процесса, вам нужно прочитать об каналах. Это обычное дело, и существуют тысячи примеров и руководств.   -  person Some programmer dude    schedule 23.05.2017
comment
Вы можете ознакомиться с исходным кодом время ожидания GNU, который делает первые две вещи, которые вам нужны. Вот что он делает: Таймер отправит родительскому SIGALRM, когда таймер истечет, и это прервет waitpid. Обработчик сигнала может вызвать kill для отправки дочернему сигналу завершения. (kill — один из допустимых системных вызовов, которые можно использовать в обработчике сигналов).   -  person Mark Plotnick    schedule 23.05.2017
comment
Что касается вывода, я все еще хочу, чтобы дочерний процесс продолжал выводить на терминал. Я просто хочу получить копию всего его вывода, когда он завершится, возможно ли что-то подобное?   -  person SFD    schedule 23.05.2017


Ответы (1)


Вы можете сделать #1 и #2 с функцией sigtimedwait и #3 с pipe:

#include <unistd.h>
#include <signal.h>
#include <iostream>

int main() {
    // Block SIGCHLD, so that it only gets delivered while in sigtimedwait.
    sigset_t sigset;
    sigemptyset(&sigset);
    sigaddset(&sigset, SIGCHLD);
    sigprocmask(SIG_BLOCK, &sigset, nullptr);

    // Make a pipe to communicate with the child process.
    int child_stdout[2];
    if(pipe(child_stdout))
        abort();

    std::cout.flush();
    std::cerr.flush();
    auto child_pid = fork();
    if(-1 == child_pid)
        abort();

    if(!child_pid) { // In the child process.
        dup2(child_stdout[1], STDOUT_FILENO); // Redirect stdout into the pipe.
        std::cout << "Hello from the child process.\n";
        std::cout.flush();
        sleep(3);
        _exit(3);
    }

    // In the parent process.
    dup2(child_stdout[0], STDIN_FILENO); // Redirect stdin to stdout of the child.
    std::string line;
    getline(std::cin, line);
    std::cout << "Child says: " << line << '\n';

    // Wait for the child to terminate or timeout.
    timespec timeout = {1, 0};
    siginfo_t info;
    auto signo = sigtimedwait(&sigset, &info, &timeout);
    if(-1 == signo) {
        if(EAGAIN == errno) { // Timed out.
            std::cout << "Killing child.\n";
            kill(child_pid, SIGTERM);
        }
        else
            abort();
    }
    else { // The child has terminated.
        std::cout << "Child process terminated with code " << info.si_status << ".\n";
    }
}

Выходы:

Child says: Hello from the child process.
Killing child.

Если sleep закомментировано:

Child says: Hello from the child process.
Child process terminated with code 3.
person Maxim Egorushkin    schedule 23.05.2017