パイプを行うシンプルなシェルを作っています。execveとパイプの問題 - 元のパイプを回復する方法?
パイプ構文を操作するためのコードです。
int fd[2];
int stdin_copy;
int stdout_copy;
int status;
char * msg;
if (pipe(fd) == -1) {
perror("pipe");
exit(1);
}
// fd[0] : process read from fd[0]
// fd[1] : process write to fd[1]
if (execok(pr_words) == 0) { /* is it executable? */
status = fork(); /* yes; create a new process */
if (status == -1) { /* verify fork succeeded */
perror("fork");
exit(1);
} else if (status == 0) { /* in the child process... */
stdout_copy = dup(1);
close(1); // close standard output
dup(fd[1]);
close(fd[0]);
close(fd[1]); // close and fd[1] will be stdout
pr_words[l_nwds] = NULL; /* mark end of argument array */
status = execve(path, pr_words, environ); /* try to execute it */
perror("execve"); /* we only get here if */
exit(0); /* execve failed... */
}
/*------------------------------------------------*/
/* The parent process (the shell) continues here. */
/*------------------------------------------------*/
else if (status > 0) { // in the parent process....
wait(& status); /* wait for process to end */
if (execok(af_words) == 0) {
if (pipe(fd2) == -1) {
perror("pipe");
exit(1);
}
status = fork();
if (status == -1) {
perror("fork");
exit(1);
} else if (status == 0) { // in the child process...
stdin_copy = dup(0);
close(0);
dup(fd[0]);
close(fd[1]);
close(fd[0]);
read(fd[0], readbuffer, sizeof(readbuffer));
af_words[r_nwds] = NULL; /* mark end of argument array */
status = execve(path, af_words, environ); /* try to execute it */
} else if (status > 0) {
wait(& status);
msg = "over";
write(2, msg, strlen(msg));
close(fd[0]);
close(fd[1]);
dup2(stdin_copy, 0);
dup2(stdout_copy, 1);
close(stdin_copy);
close(stdout_copy);
printf("%s", "hi");
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, af_words[0], strlen(af_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
}
} else {
/*----------------------------------------------------------*/
/* Command cannot be executed. Display appropriate message. */
/*----------------------------------------------------------*/
msg = "*** ERROR: '";
write(2, msg, strlen(msg));
write(2, pr_words[0], strlen(pr_words[0]));
msg = "' cannot be executed.\n";
write(2, msg, strlen(msg));
}
pr_wordsとaf_wordsは、パイプの右側と左側のコマンドを含む2次元ポインタです。
そして、まず、fork()を使って子プロセスを作り、標準出力にfd [1]を登録します。 (またstdinを閉じる前にstdinファイルディスクリプタを保存してください)。そして、コマンドの左側を実行した後、コマンドの右側を処理するための他の子プロセスを作成します。
同様に、stdoutを閉じる前にstdoutファイルディスクリプタを保存し、fd [0]標準入力を行いました。 execve関数の最初の結果からの入力を使用することによって、私はすべての結果がfd [1]に保存されると思いました。 (これは現在std出力として登録されているためです)。
最後に、パイプ入出力を標準出力に復元します。 (私はdup2を使用したくないが、私の知識が不足しているので選択肢がない)
しかし、このコードを実行すると、 'ls | cat '、出力はありません。さらに、私はターミナルのすべてのエントリを設定して '#'を印刷します。 (これは '#ls'または '#cat' ...を意味します)しかし、pipeコマンドの上に入力した後でも、そのプログラムは '#'を出力しません。
パイプコマンドを処理した後、このプログラムの入出力ストリームが完全にねじれているようです。
どうすれば修正できますか?つまり、最初のexecveの結果をfd [1]に保存し、このfd [1]を使用して2番目のexecveを実行した後、最終結果をstdoutファイルの説明で出力します。
しかし、私はあなたの答えを読んだ後にもう一つ質問があります。最初のものを除いて他のすべては理解できる。あなたはfork()関数を連続して書いておきたいですか? status1 = fork(); status2 = fork();そんな? –
いいえ、fork()を呼び出す方法を変更しないでください、その部分は問題ありません。 wait(&status)コールをfork()の間に移動して、プロセスの終わりに向かってwait(&status)を2回続けて呼び出す必要があります。 –