ここでは、2 つの子プロセス間で正しいパイピングを行う方法を理解しようとしています。ある Linux コマンドの出力を別のコマンド (ls から cat へ) に渡して、プログラムを正常に戻そうとしているだけです。ただし、フォークされた2番目の子は動けなくなり、親はこの子を永遠に待っていると推測しています。私は長い間、このコードをいじって、なぜ動かなくなったのかを突き止めようとしてきました。私は C システム プログラミングに関しては初心者ですが、勉強しようとしています。
プログラムが終了せず、猫にハングアップする理由を知っている人はいますか?
どんな助けでも大歓迎です。
ありがとう。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
int old_std_out = dup(1);
int old_std_in = dup(0);
dup2(fd[1], 1);
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(a[0], a, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_out, 1);
break;
}
dup2(fd[0], 0);
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
break;
case 0:
execve(b[0], b, envp);
exit(0);
break;
default:
waitpid(-1, &status, 0);
dup2(old_std_in, 0);
break;
}
printf("\n");
return 0;
}
プログラムには 2 つの潜在的なデッドロックがあります。
まず、最初の子 ( ls
) はパイプに書き込もうとしてブロックする可能性があります。その場合、終了するまでwaitpid()
戻らず、2 番目の子 ( ) が実行を開始するまで終了しません。これは、戻るまで発生しません。=> デッドロック。ls
ls
cat
waitpid()
第二に、書き込み終了のすべてのファイル記述子が閉じられるまで、cat
そこから読み取りstdin
ます。親プロセスとcat
書き込み終了のコピーの両方が、明示的に知らなくcat
てもそれを持っています。一部のオペレーティング システムではread()
、書き込みエンドの唯一のコピーが同じプロセス内にある場合 (このデッドロックを回避するため) はブロックされませんが、これは保証されません。いずれにせよ、親プロセスはファイル記述waitpid()
子のコピーを保持し、親プロセスはパイプの書き込み終了を待つ子プロセスを保持するため、再びデッドロックが発生します。
多くの場合、プログラムを単純化すると、次のような問題が解決します。
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
int main()
{
char *a[2] = {"/bin/ls", NULL};
char *b[2] = {"/bin/cat", NULL};
char *envp[2] = {getenv("PATH"), NULL};
int fd[2], status;
pipe(fd);
//int old_std_out = dup(1); /*No need to copy stdout...*/
//int old_std_in = dup(0); /*...or stdin...*/
//dup2(fd[1], 1); /*...if you wait dup2()ing until you need to*/
int pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[1], STDOUT_FILENO); /*NOW we dup2()*/
close(fd[0]); /*no need to pass these file descriptors to...*/
close(fd[1]); /*...a program that doesn't expect to have them open*/
execve(a[0], a, envp);
exit(0); /*might want an error message*/
//break; /*unreachable*/
default:
//waitpid(-1, &status, 0); /*don't wait yet*/
//dup2(old_std_out, 1);
close(fd[1]); /*we don't need this in the parent anymore*/
break;
}
//dup2(fd[0], 0); /*not needed anymore*/
pid = fork();
switch(pid)
{
case -1:
perror("Forkscrew");
/*might want to ensure the first child can terminate*/
exit(1);
//break; /*unreachable*/
case 0:
dup2(fd[0], STDIN_FILENO);
close(fd[0]); /*again, cat doesn't expect a fourth fd open*/
execve(b[0], b, envp);
/*again, error message would be nice*/
exit(0);
//break;
default:
//waitpid(-1, &status, 0);
//dup2(old_std_in, 0);
break;
}
waitpid(-1, &status, 0); /*don't wait until both children are created*/
waitpid(-1, &status, 0);
printf("\n");
return 0;
}
ご覧のとおり、改善のための多くの提案を残しましたが、execve()
s が正常に機能していれば、これで既に正常に動作するはずです。
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加