0

複数のパイプを使用してシェルをシミュレートする C プログラムを作成しました。問題は、などのほとんどのコマンドを実行できますがls | cat、使用できないことですls | wcwcうまくいかないケースはありますか?

int pipefd[4]; 
int p1 = pipe(pipefd);          // Open pipe 1
int p2 = pipe(pipefd + 2);      // Open pipe 2

pid_t pid;

for(i = 0; i < n_commands; i++)
{
    fflush(stdout);
    pid = fork();

    if(pid == 0)
    {
        int command_no = i;
        int prev_pipe = ((command_no - 1) % 2) * 2;
        int current_pipe = (command_no % 2) * 2;

        // If current command is the first command, close the
        // read end, else read from the last command's pipe
        if(command_no == 0)
        {
            close(pipefd[0]);
        }
        else
        {
            dup2(pipefd[prev_pipe], 0);
            close(pipefd[current_pipe]);
        }

        // If current command is the last command, close the
        // write end, else write to the pipe
        if(command_no == n_commands - 1)
        {
            close(pipefd[current_pipe + 1]);
        }
        else
        {
            dup2(pipefd[current_pipe + 1], 1);
        }

        int p = execvp(tokens[cmd_pos[command_no]], tokens + cmd_pos[command_no]);

        close(pipefd[current_pipe]);
        close(pipefd[prev_pipe]);
        close(pipefd[prev_pipe + 1]);
        close(pipefd[current_pipe + 1]);

        _exit(0);
    }
}

/usr/binパイプラインの最初のコマンドでない場合、からのプログラムは実行されていないようです。

4

2 に答える 2

1

パイプを正しく接続していません。

このロジック:

int prev_pipe = ((command_no - 1) % 2) * 2;
int current_pipe = (command_no % 2) * 2;

動作しません—モジュロの結果は常にまたはであるため、またはのいずれ0かになります...1prev_pipecurrent_pipe02

パイプを作成するコードを貼り付けなかったために、いくつかの隠された概念が欠落している場合を除きます。

于 2012-08-26T21:43:06.473 に答える
1

これは、コードから作成された非常に単純なプログラムです。パイプがどのように作成されるかを推測し、コマンドargv処理を少し単純化します。

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

static char *argv_ls[] = { "ls", 0 };
static char *argv_wc[] = { "wc", 0 };
static char **cmds[]   = { argv_ls, argv_wc };

int main(void)
{
    int n_commands = 2;
    int pipefd[2];

    pipe(&pipefd[0]);   // Error check!

    fflush(stdout);
    for (int i = 0; i < n_commands; i++)
    {
        int pid = fork();

        if (pid == 0)
        {
            int command_no = i;
            int prev_pipe = ((command_no - 1) % 2) * 2;
            int current_pipe = (command_no % 2) * 2;
            printf("cmd %d: prev pipe %d, curr pipe %d\n", i, prev_pipe, current_pipe);
            fflush(stdout);

            // If current command is the first command, close the
            // read end, else read from the last command's pipe
            if (command_no == 0)
            {
                close(pipefd[0]);
            }
            else
            {
                dup2(pipefd[prev_pipe], 0);
                close(pipefd[current_pipe]);  // Line 40
            }

            // If current command is the last command, close the
            // write end, else write to the pipe
            if (command_no == n_commands - 1)
                close(pipefd[current_pipe + 1]);  // Line 46
            else
                dup2(pipefd[current_pipe + 1], 1);

            execvp(cmds[i][0], cmds[i]);
            fprintf(stderr, "Failed to exec: %s (%d: %s)\n", cmds[i][0], errno, strerror(errno));
            _exit(1);
        }
    }

    return 0;
}

GCC 4.7.1 (Mac OS X 10.7.4 上) でコンパイルすると、次の警告が表示されます。

pipes-12133858.c: In function ‘main’:
pipes-12133858.c:40:22: warning: array subscript is above array bounds [-Warray-bounds]
pipes-12133858.c:46:22: warning: array subscript is above array bounds [-Warray-bounds]

実行すると、次の出力が得られます。

Isis JL: pipes-12133858
cmd 0: prev pipe -2, curr pipe 0
cmd 1: prev pipe 0, curr pipe 2
Isis JL: wc: stdin: read: Bad file descriptor

コード内の親は子が終了するのを待たないため、 からのエラー メッセージの前にプロンプ​​トが表示されますwcが、出力された診断番号は、あらゆる種類の問題があることを示しています (コンパイラは問題のいくつかを特定できました)。 .

exec*()どの関数ファミリからの戻り値もチェックする必要がないことに注意してください。彼らが成功した場合、彼らは戻ってきません。彼らが戻ってきた場合、彼らは失敗しました。_exit(0);いずれにせよシステムはそれらを閉じるため、呼び出す前に閉じる必要もありませんでした。また、何かの実行に失敗した場合は、実行に失敗したことを示すメッセージを出力し、ゼロ以外の終了ステータスで終了するのが礼儀です。

したがって、Michał Górnyが言うように、あなたの問題の大部分は、あなたがそれを示しておらず、おそらく間違っているため、パイプ処理コードが少なくとも不可解であるということです。

close()また、コードに十分な呼び出しがないこともかなり確信しています。ガイドラインとして、パイプラインの一部となるパイプを開いている各プロセスでは、pipe()システム コールによって返されたすべてのファイル記述子を、特定の子プロセスがexec*()関数を使用する前に閉じる必要があります。パイプを閉じないと、パイプの書き込み側が開いているため、プロセスがハングする可能性があります。書き込み側が開いているプロセスが、パイプの読み取り側から読み取ろうとしているプロセスである場合、読み取るデータは見つかりません。

于 2012-08-27T04:16:14.190 に答える