Возможные дубликаты:
Я пытался изучить пайпинг в Linux с использованием dup / dup2 и разветвления последние 3 дня. Я думаю, что у меня это получилось, но когда я вызываю две разные программы из дочернего процесса, мне кажется, что я получаю вывод только из первой вызванной программы. Я не понимаю, почему это и / или что я делаю неправильно. Это мой основной вопрос.
Изменить: Я думаю, что возможное решение состоит в том, чтобы разветвить другого потомка и настроить каналы с 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
Исходный код двоичного файла «externalr» (закомментированный выше) просто:
#include <iostream>
int main(){
std::cout << "Hello from stdout" << std::endl;
std::cerr << "Hello from stderr!" << std::endl;
return 0;
}
Когда я вызываю «externalr» вместо 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);