質問
パイプを使用してシェル(CMD)インターフェイスをエミュレートするプログラムを準備しました。プログラムには2つのバージョンがあります。1。1つのパイプを使用します(親から子へのパイプを使用)2。ダブルパイプを使用します(親から子供、子供への2つのパイプを使用して、通信します)。
したがって、最初のプログラムは望ましいインターフェイスを提供し、私が望む方法を機能させますが、2番目のプログラムで同じ結果(インターフェイス)に到達することはできません(dup2()などを使用)。
それで、私はあなたの助けを伝え、両方のコードを以下に置きます。
BS:これらのコマンドを使用して同じ方法で両方のプログラムをコンパイルして試すことができます。
$ gcc prog1.c -o prog1
次に実行しましょう:
$ ./PROG1
次に、新しいターミナルを実行して、入力にデータを作成してみてください。txt:
$ echo pwd> input.txt
そして、最初の端末で結果を視聴します。
(これは最初のプログラムでは正常に機能しますが、2番目のプログラムで同じインターフェイスでこれを機能させる必要があります)
最初のプログラムのコード(正常に動作):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
void do_child(int data_pipe[]) {
int c;
int rc;
close(data_pipe[1]);
dup2(data_pipe[0], 0); /* This string provides the desired interface of the program */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while ((rc = read(data_pipe[0], &c, 1)) > 0)
{
putchar(c);
}
exit(0);
}
void do_parent(int data_pipe[])
{
int c;
int rc;
FILE *in;
close(data_pipe[0]);
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0)
{
rc = write(data_pipe[1], &c, 1);
if (rc == -1)
{
perror("Parent: write");
close(data_pipe[1]);
exit(1);
}
}
fclose(in);
}
close(data_pipe[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int data_pipe[2];
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(data_pipe);
if (rc == -1)
{
perror("pipe");
exit(1);
}
pid = fork();
switch (pid)
{
case -1:
perror("fork");
exit(1);
case 0:
do_child(data_pipe);
default:
do_parent(data_pipe);
}
return 0;
}
2番目のプログラムのコード(少し修正する必要があります):
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
/* Original version got from http://www.iakovlev.org */
int parent_to_child[2];
int child_to_parent[2];
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
void do_child()
{
int c;
char ch;
int rc;
close(parent_to_child[1]); /* we don't need to write to this pipe. */
close(child_to_parent[0]); /* we don't need to read from this pipe. */
//dup2(parent_to_child[0], STDIN_FILENO);
//dup2(child_to_parent[1], STDOUT_FILENO);
/* Some dup2() routines must be added here
to get this working as the first program above */
char* cmd[] = { "bash", (char *)0 };
execvp("bash", cmd);
while (read(parent_to_child[0], &ch, 1) > 0) {
c = (int)ch;
ch = (char)c;
putchar(ch);
rc = write(child_to_parent[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(1);
}
}
close(parent_to_child[0]);
close(child_to_parent[1]);
exit(0);
}
int main(int argc, char* argv[])
{
int pid;
int rc;
umask(0);
mknod("input.txt", S_IFIFO|0666, 0);
rc = pipe(parent_to_child);
if (rc == -1) {
perror("main: pipe parent_to_child");
exit(1);
}
rc = pipe(child_to_parent);
if (rc == -1) {
perror("main: pipe child_to_parent");
exit(1);
}
pid = fork();
switch (pid) {
case -1:
perror("main: fork");
exit(1);
case 0:
do_child();
default:
do_parent();
}
return 0;
}
解決
大きな違いはここにあります:
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
/* .... */
/* read back from child */
rc = read(child_to_parent[0], &ch, 1);
/* .... */
putchar(c);
}
私はあなたのためにコンパイル/テストするのが面倒なので、私は単に親がread()でブロックされていると推測します。反対側(子どものプロセスのバッシュ)は、すべての書かれたキャラクターを反映することを保証されていないためです。または、あなたのコードが処理できないものを複数の文字を印刷することさえ決定するかもしれません。
ケースでは、読むべきものがあるかどうかを確認するために()ポーリングする必要があります。または、fcntl(f_setfl)を使用して、child_to_parent [0]にo_nonblockフラグを設定し、errno == eagainの場合は、read()branchをスキップするだけです。まだ読むキャラクターがある間にループします。
編集1。 ところで、私は完全にパーツを逃しました:あなたのdo_parent()ループは両方でpoll()を使用する必要があります child_to_parent[0]
と in
, 、反対側が何かを書くかもしれないので(read()はブロックしません)、それに文字を書き込まない場合でも。
他のヒント
あなたのおかげで、私はそれが機能しているようです。
したがって、ここにdo_parentの更新されたコードがあります:
void do_parent()
{
int c;
char ch;
int rc;
FILE *in;
struct pollfd fds[2];
int pol_ret;
fds[0].fd = child_to_parent[0];
close(child_to_parent[1]); /* we don't need to write to this pipe. */
close(parent_to_child[0]); /* we don't need to read from this pipe. */
while (1)
{
in = fopen("input.txt", "r");
fds[1].fd = fileno(in);
pol_ret = poll(fds, 2, 500);
while ((c = fgetc(in)) > 0) {
ch = (char)c;
/* write to child */
rc = write(parent_to_child[1], &ch, 1);
if (rc == -1) {
perror("child: write");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
/* read back from child */
if (fds[0].revents & POLLIN)
{
rc = read(child_to_parent[0], &ch, 1);
c = (int)ch;
if (rc <= 0) {
perror("parent: read");
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(1);
}
putchar(c);
}
}
fclose(in);
}
close(child_to_parent[0]);
close(parent_to_child[1]);
exit(0);
}
また、これをdo_child()に追加しました。
dup2(parent_to_child[0], STDIN_FILENO);