[英]why can't I pipe output from both execl calls?
可能重復:
我一直在嘗試使用dup / dup2在Linux中學習管道,並在最近3天進行了分叉。 我想我掌握了這個要訣,但是當我從子進程中調用兩個不同的程序時,看來我只是從第一個調用的程序中捕獲輸出。 我不明白為什么會這樣和/或我做錯了什么。 這是我的主要問題。
編輯:我認為可能的解決方案是派生另一個孩子並使用dup2設置管道,但是我只是想知道為什么下面的代碼不起作用。 我的意思是,我希望從第一個execl調用捕獲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
“外部”二進制文件的源代碼(上面已注釋)很簡單:
#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
一旦成功調用exec
系列中的execl
或任何其他函數,新進程將完全覆蓋原始進程。 這意味着新過程永遠不會“返回”到舊過程。 如果您連續有兩個execl
調用,則第二個execl
的唯一方法是第一個失敗。
為了連續運行兩個不同的命令,您必須fork
一個孩子來運行第一個命令, wait
, fork
另一個孩子來運行第二個命令,然后(可選)也wait
第二個孩子。
read
read
系統調用不會附加終止的null,因此通常您需要查看返回值,該值告訴您實際讀取的字節數。 然后將以下字符設置為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 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.