可能重复:
我一直在尝试使用dup / dup2来学习Linux中的管道并在最近3天内进行分叉。我想我已经掌握了它,但是当我从子进程中调用两个不同的程序时,我似乎只捕获了第一个被调用的输出。 我不明白为什么会这样做和/或我做错了什么。这是我的主要问题。
编辑:我认为一个可能的解决方案是分叉另一个孩子并使用dup2设置管道,但我更想知道为什么下面的代码不起作用。我的意思是,我希望从第一个execl调用和stdout中捕获stderr。这似乎没有发生。
我的第二个问题是我是否正确打开和关闭管道。如果没有,我想知道我需要添加/删除/更改。
这是我的代码:
#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!
答案 0 :(得分:3)
在execl
成功调用execl
或exec
系列中的任何其他功能后,原始流程将被新流程完全覆盖。这意味着新流程永远不会回归&#34;对旧的。如果连续有两个execl
个调用,则第二个调用的唯一方法是第一个调用失败。
为了连续运行两个不同的命令,你必须fork
一个孩子运行第一个命令,wait
,fork
第二个孩子来运行第二个命令,然后(可选)wait
为第二个孩子。
在read
read
系统调用不附加终止空值,因此通常需要查看返回值,该值告诉您实际读取的字节数。然后将以下字符设置为null以获取C字符串,或使用std::string
的范围构造函数。
在管道上
现在您正在使用waitpid
等待子进程已经完成,然后从管道读取。这样做的问题是,如果子进程产生大量输出,那么它将阻塞,因为管道已满并且父进程没有从中读取。当孩子等待父母阅读时,结果将是死锁,并且父母等待孩子终止。
您应该使用select
等待输入到达孩子的stdout
或孩子的stderr
。输入到达时,请阅读;这将允许孩子继续。当子进程终止时,您将会知道,因为您将在两者上获得文件结束。 然后您可以安全地拨打wait
或waitpid
。
答案 1 :(得分:3)
exec
系列函数用新的过程映像替换当前过程映像。执行时,
execl("/bin/bash", "/bin/bash", "foo", NULL);
当前进程的代码不再执行。这就是为什么你永远不会看到执行结果
的原因execl("/bin/ls", "ls", "-1", (char *)0);