почему я не могу передать вывод обоих вызовов execl?

StackOverflow https://stackoverflow.com//questions/25026277

  •  21-12-2019
  •  | 
  •  

Вопрос

Возможные дубликаты:

Последние 3 дня я пытался изучить конвейерную обработку в Linux, используя dup/dup2 и fork.Думаю, я это понял, но когда я вызываю две разные программы из дочернего процесса, кажется, что я захватываю вывод только первой вызванной программы. Я не понимаю, почему это так и/или что я делаю неправильно.Это мой основной вопрос.

Редактировать:Я думаю, что возможное решение — создать еще один дочерний элемент и настроить каналы с помощью dup2, но мне больше всего интересно, почему приведенный ниже код не работает.Я имею в виду, что я ожидаю захвата stderr из первого вызова execl и stdout из второго.Кажется, этого не происходит.

Мой второй вопрос: правильно ли я открываю и закрываю трубы.Если нет, то я хотел бы знать, что мне нужно добавить/удалить/изменить.

Вот мой код:

#include <stdlib.h>
#include <iostream>
#include <time.h>
#include <sys/wait.h>

#define READ_END 0
#define WRITE_END 1

void parentProc(int* stdoutpipe, int* stderrpipe);
void childProc(int* stdoutpipe, int* stderrpipe);

int main(){
    pid_t pid;
    int status;

    int stdoutpipe[2]; // pipe
    int stderrpipe[2]; // pipe

    // create a pipe
    if (pipe(stdoutpipe) || pipe(stderrpipe)){
        std::cerr << "Pipe failed." << std::endl;
        return EXIT_FAILURE;
    }

    // fork a child
    pid = fork();
    if (pid < 0) {
        std::cerr << "Fork failed." << std::endl;
        return EXIT_FAILURE;
    } 
    // child process
    else if (pid == 0){
        childProc(stdoutpipe, stderrpipe);  

    }
    // parent process
    else {
        std::cout<< "waitpid: " << waitpid(pid, &status, 0)
             <<'\n'<<std::endl;
        parentProc(stdoutpipe, stderrpipe);
    }

    return 0;
}



void childProc(int* stdoutpipe, int* stderrpipe){
    dup2(stdoutpipe[WRITE_END], STDOUT_FILENO);
    close(stdoutpipe[READ_END]);

    dup2(stderrpipe[WRITE_END], STDERR_FILENO);
    close(stderrpipe[READ_END]);

    execl("/bin/bash", "/bin/bash", "foo", NULL);
    execl("/bin/ls", "ls", "-1", (char *)0);

    //  execl("/home/me/outerr", "outerr", "-1", (char *)0);

    //char * msg = "Hello from stdout";
    //std::cout << msg;

    //msg = "Hello from stderr!";
    //std::cerr << msg << std::endl;

    // close write end now?
}

void parentProc(int* stdoutpipe, int* stderrpipe){
    close(stdoutpipe[WRITE_END]);
    close(stderrpipe[WRITE_END]);

    char buffer[256];
    char buffer2[256];
    read(stdoutpipe[READ_END], buffer, sizeof(buffer));
    std::cout << "stdout: " << buffer << std::endl;

    read(stderrpipe[READ_END], buffer2, sizeof(buffer));
    std::cout << "stderr: " << buffer2 << std::endl;

    // close read end now?
}

Когда я запускаю это, я получаю следующий вывод:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21423

stdout: hB�(6
stderr: foo: line 1: -bash:: command not found

Исходный код двоичного файла "outerr" (закомментирован выше) выглядит просто:

#include <iostream>

int main(){
    std::cout << "Hello from stdout" << std::endl;
    std::cerr << "Hello from stderr!" << std::endl;
    return 0;
}

Когда я вызываю «outerr» вместо ls или «foo», я получаю следующий ожидаемый результат:

yfp> g++ selectTest3.cpp; ./a.out 
waitpid: 21439

stdout: Hello from stdout

stderr: Hello from stderr!
Это было полезно?

Решение

На execl

Как только вы успешно позвоните execl или любую другую функцию из exec семьи, исходный процесс полностью перезаписывается новым процессом.Это означает, что новый процесс никогда не «возвращается» к старому.Если у вас есть два execl вызовы подряд, второй может быть выполнен только в случае сбоя первого.

Чтобы запустить две разные команды подряд, вам нужно fork один ребенок для запуска первой команды, wait, fork второй дочерний элемент для запуска второй команды, затем (необязательно) wait для второго ребенка тоже.

На read

А read системный вызов не добавляет завершающий нуль, поэтому обычно вам нужно смотреть на возвращаемое значение, которое сообщает вам количество фактически прочитанных байтов.Затем установите для следующего символа значение null, чтобы получить строку C, или используйте конструктор диапазона для std::string.

На трубах

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

Что вам следует сделать, это использовать select дождаться поступления ввода на дочерний компьютер stdout или ребенка stderr.Когда поступит ввод, прочитайте его;это позволит ребенку продолжить.Когда дочерний процесс завершится, вы узнаете об этом, потому что в обоих случаях вы получите конец файла. Затем ты можешь спокойно позвонить wait или waitpid.

Другие советы

Семья функций exec Замените текущий процесс процесса с новым процессором изображения.Когда вы выполняете,

execl("/bin/bash", "/bin/bash", "foo", NULL);
.

Код из текущего процесса больше не выполняется.Вот почему вы никогда не увидите результат выполнения

execl("/bin/ls", "ls", "-1", (char *)0);
.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top