Есть ли способ отсоединить процесс от исходящего потока через некоторое время? - PullRequest
2 голосов
/ 28 октября 2019

Я использую boost :: process :: child для создания нового процесса. Время начала процесса, которое я запускаю, не является мгновенным, поэтому мне придется подождать некоторое время до полной его инициализации.

auto is_ptr = std::make_shared<bp::ipstream>();
auto child_pr = std::make_shared<bp::child>(executable, args, bp::std_out > *is_ptr);
m_childs[port] = {child_pr, is_ptr};

std::string line;
while (child_pr->running() && std::getline(*is_ptr, line)) {
    std::cerr <<"SI: \t" << line << std::endl;
    if( 0 == line.compare(0, string_to_find.size(), string_to_find)){
        break;
    }
}
...

После этого цикла мне больше не нужно иметь ipstream. Есть ли способ отсоединить его от дочернего процесса?

1 Ответ

1 голос
/ 08 ноября 2019

Поскольку вы попросили предоставить ответ, я добавлю сюда дополнительную информацию, хотя я не уверен, что она полностью ответит на ваш вопрос.

Если предполагается, что целевой платформой является Linux, после уничтожения ipstreamв родительском процессе это фактически означает, что файловый дескриптор для связанного канала между родительским и дочерним процессом закрыт в родительском процессе. Как только дочерний процесс выполняет запись в канал после того, как родительский процесс закрыл его конец чтения канала, для дочернего процесса создается SIGPIPE, что приводит к его завершению в случае, если дополнительные меры не предпринимаются.

Чтобы предотвратить это, один из вариантов - игнорировать SIGPIPE у дочернего элемента. Теперь это приведет к ошибкам в дочернем процессе при записи в этот канал. Это будет зависеть от реализации дочернего процесса. Решением в вашем случае может быть игнорирование SIGPIPE и принятие мер в дочернем процессе, если он больше не может успешно записывать данные, чтобы предотвратить много потерянных циклов ЦП.

Чтобы поэкспериментировать с этим нанижний уровень, вы можете использовать следующую программу. Он разветвляет дочерний процесс, который будет продолжать запись в некоторый вывод, пока это будет успешно выполнено. Родительский процесс закроет соответствующий канал, как только получит из него некоторые данные.

Поведение программы отличается в зависимости от того, как обрабатывается SIGPIPE в дочернем процессе. В случае, если он игнорируется, write() в дочернем процессе завершится ошибкой, и дочерний процесс завершит работу с ненулевым кодом завершения. Если SIGPIPE не игнорируется, дочерний процесс завершается операционной системой. Родительский процесс расскажет вам, что произошло в дочернем процессе.

#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(int argc, char** argv)
{
    int pipe_fds[2];
    if (pipe(pipe_fds) < 0) {
        perror("pipe");
        exit(1);
    }

    pid_t pid;
    if ((pid = fork()) < 0) {
        perror("fork");
        exit(1);
    }

    if (pid == 0)
    {
        close(pipe_fds[0]); /* close read-end in the child */

        /* Uncomment the following line, and the child will terminate as soon
           as the parent closes the read end of the pipe...This is here merely
           for illustrative purposes, production code should use either
           sigaction() or pthreads related signal functionality in case of a
           multi-threaded program. */

        /* signal(SIGPIPE, SIG_IGN); */

        /* Child process, start writing to the write-end of the pipe. */
        const char message[] = "Hello world!\n";
        while (write(pipe_fds[1], message, strlen(message)) >= 0);

        exit(1);
    }

    close(pipe_fds[1]);
    char buf[256];
    ssize_t count;
    while ((count = read(pipe_fds[0], buf, sizeof(buf) - 1)) == 0);
    if (count < 0) {
        perror("read");
        exit(1);
    }

    buf[count] = '\0';
    printf("%s", buf);

    /* Close read-end in the parent, this will trigger SIGPIPE in the child
       once the child writes to the pipe. */
    close(pipe_fds[0]);

    int stat;
    if (waitpid(pid, &stat, 0) < 0) {
        perror("waitpid");
        exit(1);
    }

    if (WIFSIGNALED(stat) && WTERMSIG(stat) == SIGPIPE) {
        printf("\nChild terminated by SIGPIPE\n");
    }
    if (WIFEXITED(stat)) {
        printf("\nChild exited with exit code %d\n", WEXITSTATUS(stat));
    }

    exit(0);
}
...