7

クラスのパイプラインを使用してシェルを実装するのに苦労しています。

typedef struct {
    char** cmd;
    int in[2];
    int out[2];
} cmdio;

cmdio cmds[MAX_PIPE + 1];

パイプライン内のコマンドが読み取られ、に格納されcmdsます。

cmdio[i].inによって返される入力パイプのファイル記述子のペアですpipe()。端末入力から読み取る最初のコマンドの場合、それは{fileno(stdin)、-1}です。cmdin[i].out出力パイプ/端子出力についても同様です。cmdio[i].inと同じcmd[i-1].outです。例えば:

$ ls -l | sort | wc

CMD: ls -l 
IN: 0 -1
OUT: 3 4

CMD: sort 
IN: 3 4
OUT: 5 6

CMD: wc 
IN: 5 6
OUT: -1 1

各コマンドをprocess_commandに渡します。これは、次のことを行います。

for (cmdi = 0; cmds[cmdi].cmd != NULL; cmdi++) {
    process_command(&cmds[cmdi]);
}

ここで、process_command内:

if (!(pid_fork = fork())) {
    dup2(cmd->in[0], fileno(stdin));
    dup2(cmd->out[1], fileno(stdout));    
    if (cmd->in[1] >= 0) {
        if (close(cmd->in[1])) {
            perror(NULL);
        }
    }
    if (cmd->out[0] >= 0) {
        if (close(cmd->out[0])) {
            perror(NULL);
        }
    }
    execvp(cmd->cmd[0], cmd->cmd);
    exit(-1);
}

問題は、パイプブロックからの読み取りが永久に続くことです。

COMMAND $ ls | wc
Created pipe, in: 5 out: 6
Foreground pid: 9042, command: ls, Exited, info: 0
[blocked running read() within wc]

プロセスをと交換する代わりに、次のexecvpようにします。

if (!(pid_fork = fork())) {
    dup2(cmd->in[0], fileno(stdin));
    dup2(cmd->out[1], fileno(stdout));
    if (cmd->in[1] >= 0) {
        if (close(cmd->in[1])) {
            perror(NULL);
        }
    }
    if (cmd->out[0] >= 0) {
        if (close(cmd->out[0])) {
            perror(NULL);
        }
    }

    char buf[6];
    read(fileno(stdin), buf, 5);
    buf[5] = '\0';

    printf("%s\n", buf);
    exit(0);
}

それはたまたま機能します:

COMMAND $ cmd1 | cmd2 | cmd3 | cmd4 | cmd5 
Pipe creada, in: 11 out: 12
Pipe creada, in: 13 out: 14
Pipe creada, in: 15 out: 16
Pipe creada, in: 17 out: 18
hola!
Foreground pid: 9251, command: cmd1, Exited, info: 0
Foreground pid: 9252, command: cmd2, Exited, info: 0
Foreground pid: 9253, command: cmd3, Exited, info: 0
Foreground pid: 9254, command: cmd4, Exited, info: 0
hola!
Foreground pid: 9255, command: cmd5, Exited, info: 0

何が問題なのですか?

4

2 に答える 2

4

十分な成約がありません。コード内:

if (!(pid_fork = fork())) {
    dup2(cmd->in[0], fileno(stdin));
    dup2(cmd->out[1], fileno(stdout));    
    if (cmd->in[1] >= 0) {
        if (close(cmd->in[1])) {
            perror(NULL);
        }
    }
    if (cmd->out[0] >= 0) {
        if (close(cmd->out[0])) {
            perror(NULL);
        }
    }
    execvp(cmd->cmd[0], cmd->cmd);
    exit(-1);
}

stdinstdout(を介して)にパイプを複製した後、パイプfileno()を閉じる必要があります。

if (!(pid_fork = fork())) {
    dup2(cmd->in[0], fileno(stdin));
    dup2(cmd->out[1], fileno(stdout));    
    if (cmd->in[1] >= 0) {
        if (close(cmd->in[1])) {
            perror(NULL);
        }
    }
    if (cmd->out[0] >= 0) {
        if (close(cmd->out[0])) {
            perror(NULL);
        }
    }
    close(cmd->in[0]);  // Or your error checked version, but I'd use a function
    close(cmd->out[1]);     
    execvp(cmd->cmd[0], cmd->cmd);
    exit(-1);
}

ファイルの書き込み終了がまだ開いているため、プログラムは終了していません。また、親プロセス(シェル)がパイプを作成する場合は、パイプの両端を閉じる必要があることを忘れないでください。十分なパイプを閉じないことは、パイプを使った配管を学び始めるときにおそらく最も一般的な間違いです。

于 2012-06-09T04:04:30.567 に答える
1

無事、やっと解決しました。

親プロセスでは、子全体がフォークした直後に、次のように記述しました。

f (cmd->in[0] != fileno(stdin)) {
    close(cmd->in[0]);
    close(cmd->in[1]);
} 

そしてほら。私は以前にそのようなことをしたことがありましたが、タイプミスしてclose(cmd->out[0]);代わりにしました。それだけです。今では理にかなっています。

于 2012-06-09T04:39:01.217 に答える