両方の execl 呼び出しからの出力をパイプできないのはなぜですか?
質問
重複の可能性:
私はここ 3 日間、dup/dup2 と fork を使用して Linux でのパイピングを学ぼうとしています。コツはわかったと思いますが、子プロセスから 2 つの異なるプログラムを呼び出すと、最初に呼び出されたプログラムからの出力のみをキャプチャしているようです。 それがなぜなのか、そして/または私が間違っているのか、あるいはその両方がわかりません。これが私の一番の質問です。
編集:考えられる解決策は、別の子をフォークして dup2 とのパイプを設定することだと思いますが、なぜ以下のコードが機能しないのか疑問に思っています。つまり、最初の execl 呼び出しから stderr をキャプチャし、2 番目の execl 呼び出しから stdout をキャプチャすることを期待しているということです。これは起こっていないようです。
2番目の質問は、パイプを正しく開閉しているかどうかです。そうでない場合、何を追加/削除/変更する必要があるのか知りたいです。
これが私のコードです:
#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;
}
ls または "foo" の代わりに "outerr" を呼び出すと、期待どおりの次の出力が得られます。
yfp> g++ selectTest3.cpp; ./a.out
waitpid: 21439
stdout: Hello from stdout
stderr: Hello from stderr!
解決
の上 execl
電話に成功したら execl
またはその他の機能 exec
ファミリの場合、元のプロセスは新しいプロセスによって完全に上書きされます。これは、新しいプロセスが古いプロセスに決して「戻らない」ことを意味します。2つある場合 execl
を連続して呼び出した場合、2 番目の呼び出しが実行できる唯一の方法は、最初の呼び出しが失敗した場合です。
2 つの異なるコマンドを続けて実行するには、次のことを行う必要があります。 fork
1 人の子が最初のコマンドを実行します。 wait
, fork
2 番目の子で 2 番目のコマンドを実行し、その後 (オプションで) 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);