0
  1. pipe2 つの子プロセスの間に を作成しました。まず、ls適切な fd に書き込む を実行grep rし、適切な fd から読み取るを実行します。

  2. コマンドが正常に機能することを端末で確認できますgrep(出力)

  3. 問題は、もう実行されていなくてgrepも、終了せず、そこにとどまることですls

他のプログラムでは正常にpipe動作します..

for (i = 0; i < commands_num ; i++) {   //exec all the commands instants
    if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary 
        if (pipe(pipe_fd) == -1) {
            perror("Error: \"pipe()\" failed");
        }
        pcommands[i]._fd_out = pipe_fd[1];
        pcommands[i+1]._fd_in = pipe_fd[0]; 
    }
    pid = fork();   //the child exec the commands  
    if (pid == -1) {
        perror("Error: \"fork()\" failed");
        break;          
    } else if (!pid) { //child process

        if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
            if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_in);
        }

        if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
            if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                perror("Error: \"dup2()\" failed");
                exit(0);
            }
            close(pcommands[i]._fd_out);
        } 
        execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

        perror("Error: \"execvp()\" failed");
        exit(0);
    } else if (pid > 0) { //father process
    waitpid(pid, NULL, WUNTRACED);
    }
}
//closing all the open fd's
for (i = 0; i < commands_num ; i++) {
    if (pcommands[i]._fd_in != STDIN) { //if there was an other stdin that is not 0
        close(pcommands[i]._fd_in);
    }           
    if (pcommands[i]._fd_out != STDOUT) { //if there was an other stdout that is not 1
        close(pcommands[i]._fd_out);            
    }   
}

だから、私は "コマンド" インスタントpcommands[i] を持っています。これには、pipein、pipeout fdin、fdout のフラグ、および char** ("ls -l" のような実際のコマンドの場合) があります。

すべてが良いとしましょう。つまり、次のことを意味します。

pcommands[0]:
pipein=0
pipeout=1
char** = {"ls","-l",NULL}

pcommands[1]:
pipein=1
pipeout=0
char** = {"grep","r",NULL}

これで、ループが 2 回実行されます (コマンド インスタントが 2 つあるため) 最初に、pcommands[0] has pipeout==1 create pipe do fork pcommands[0] has pipeout==1 child: dup2 to が表示されます。 stdout execvp

2 回目: パイプを作成しません。子をフォークします: pcomands[1] には pipein==1 があります。

このコマンドは機能します。私の出力は次のとおりです。

errors.log exer2.pdf multipal_try

(「r」を含むすべてのもの)しかし、その後、スタックし、抜け出せませんgrep..私が見ることができる他の端末でgrepは、まだ動作しています

閉じる必要があるすべての fd を閉じることを願っています...

うまくいかない理由がわかりません。正しく動作しているように見えます (まあ、他のコマンドでも動作します..)

誰か助けてくれませんか?ありがとう

4

1 に答える 1

2

十分なパイプ ファイル記述子を閉じていません。

経験則:

  • dup()またはを使用してパイプ ファイル記述子を標準入力または標準出力に複製する場合は、元のパイプ ファイル記述子の両方dup2()を閉じる必要があります。

また、親シェルがパイプを作成する場合は、パイプ ファイル記述子の両方のコピーを閉じる必要があります。

また、パイプライン内のプロセスは同時に実行できるようにする必要があることに注意してください。特に、パイプには限られた容量があり、パイプに空きがなくなるとプロセスがブロックされます。制限は非常に小さい場合があります (POSIX では、少なくとも 4 KiB である必要がありますが、それだけです)。プログラムがメガバイト単位のデータを処理する場合、パイプラインで同時に実行できるようにする必要があります。したがって、waitpid()子を起動するループの外側で発生する必要があります。また、待機する前に親プロセスのパイプを閉じる必要があります。そうしないと、パイプを読んでいる子は決して EOF を見ることはありません (理論的には、親はパイプに書き込めませんが、書き込めないからです)。

名前がアンダースコアで始まる構造体メンバーがあります。それは危険です。アンダースコアで始まる名前は、実装用に予約されています。C標準は次のように述べています。

ISO/IEC 9899:2011 §7.1.3 予約済み識別子

— アンダースコアと大文字または別のアンダースコアで始まるすべての識別子は、常に使用のために予約されています。
— アンダースコアで始まるすべての識別子は、通常の名前空間とタグ名空間の両方で、ファイル スコープの識別子として使用するために常に予約されています。

つまり、問題が発生した場合、それはシステムの問題ではなく、あなたの問題です。明らかに、コードは機能しますが、遭遇する可能性のある問題に注意する必要があり、それらを回避することが最も賢明です。


サンプルコード

これは、上記のコードに基づく固定 SSCCE です。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <unistd.h>

typedef struct Command Command;
struct Command
{
    int _fd_out;
    int _fd_in;
    int _flag_pipe_in;
    int _flag_pipe_out;
    char **_commands;
};

typedef int Pipe[2];

enum { STDIN = STDIN_FILENO, STDOUT = STDOUT_FILENO, STDERR = STDERR_FILENO };

int main(void)
{
    char *ls_cmd[] = { "ls", 0 };
    char *grep_cmd[] = { "grep", "r", 0 };
    Command commands[] =
    {
        {
            ._fd_in  = 0, ._flag_pipe_in  = 0,
            ._fd_out = 1, ._flag_pipe_out = 1,
            ._commands = ls_cmd,
        },
        {
            ._fd_in  = 0, ._flag_pipe_in  = 1,
            ._fd_out = 1, ._flag_pipe_out = 0,
            ._commands = grep_cmd,
        }
    };
    int commands_num = sizeof(commands) / sizeof(commands[0]);

    /* Allow valgrind to check memory */
    Command *pcommands = malloc(commands_num * sizeof(Command));
    for (int i = 0; i < commands_num; i++)
        pcommands[i] = commands[i];

    for (int i = 0; i < commands_num; i++) {   //exec all the commands instants
        if (pcommands[i]._flag_pipe_out == 1) { //creates pipe if necessary
            Pipe pipe_fd;
            if (pipe(pipe_fd) == -1) {
                perror("Error: \"pipe()\" failed");
            }
            pcommands[i]._fd_out = pipe_fd[1];
            pcommands[i+1]._fd_in = pipe_fd[0];
        }
        pid_t pid = fork();   //the child exec the commands
        if (pid == -1) {
            perror("Error: \"fork()\" failed");
            break;
        } else if (!pid) { //child process

            if (pcommands[i]._flag_pipe_in == 1) {  //if there was a pipe to this command
                assert(i > 0);
                assert(pcommands[i-1]._flag_pipe_out == 1);
                assert(pcommands[i-1]._fd_out > STDERR);
                if (dup2(pcommands[i]._fd_in, STDIN) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_in);
                close(pcommands[i-1]._fd_out);
            }

            if (pcommands[i]._flag_pipe_out == 1) { //if there was a pipe from this command
                assert(i < commands_num - 1);
                assert(pcommands[i+1]._flag_pipe_in == 1);
                assert(pcommands[i+1]._fd_in > STDERR);
                if (dup2(pcommands[i]._fd_out, STDOUT) == -1) {
                    perror("Error: \"dup2()\" failed");
                    exit(0);
                }
                close(pcommands[i]._fd_out);
                close(pcommands[i+1]._fd_in);
            }
            execvp(pcommands[i]._commands[0] , pcommands[i]._commands); //run the command

            perror("Error: \"execvp()\" failed");
            exit(1);
        }
        else
            printf("Child PID %d running\n", (int)pid);
    }

    //closing all the open pipe fd's
    for (int i = 0; i < commands_num; i++) {
        if (pcommands[i]._fd_in != STDIN) { //if there was another stdin that is not 0
            close(pcommands[i]._fd_in);
        }
        if (pcommands[i]._fd_out != STDOUT) { //if there was another stdout that is not 1
            close(pcommands[i]._fd_out);
        }
    }

    int status;
    pid_t corpse;
    while ((corpse = waitpid(-1, &status, 0)) > 0)
        printf("Child PID %d died with status 0x%.4X\n", (int)corpse, status);

    free(pcommands);

    return(0);
}

私の知る限り、「紛れもなく乱雑」にならないようにするにはどうすればよいですか?

子がアサートに含まれる条件について心配する必要がないように、おそらくパイプ情報を保持します (パイプラインで子の子情報にアクセスする前または後にアクセスします)。各子が独自のデータ構造内の情報にアクセスするだけでよい場合、よりクリーンになります。「struct コマンド」を再編成して、2 つのパイプと、どのパイプに閉じる必要がある情報が含まれているかを示すインジケーターを含めます。多くの点で、あなたが持っているものと根本的に異なるわけではありません。その子のほうがきちんとしていて見ればいいだけpcommands[i]です。

C Minishell パイプラインの追加で、別のコンテキストで部分的な回答を確認できます。

于 2012-12-03T23:18:05.380 に答える