почему я не могу передать вывод обоих вызовов execl?
Вопрос
Возможные дубликаты:
- Как вызвать execl() в C с правильными аргументами?
- Получение вывода из exec
- Linux Pipes как ввод и вывод
- Использование dup2 для конвейера
- Трубопроводы для ввода/вывода
Последние 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);
.