3

実際に機能するcでパイプラインを実装する方法は考えられません。ということで、ここに書き込むことにしました。パイプ/フォーク/mkfifo がどのように機能するかを理解していると言わざるを得ません。2 ~ 3 個のパイプラインを実装する例をたくさん見てきました。それは簡単です。私の問題は、シェルを実装する必要があるときに始まり、パイプラインの数が不明です。

私が今持っているもの:例えば。

ls -al | tr a-z A-Z | tr A-Z a-z | tr a-z A-Z

私はそのような行をそのようなものに変換します:

array[0] = {"ls", "-al", NULL"}
array[1] = {"tr", "a-z", "A-Z", NULL"}
array[2] = {"tr", "A-Z", "a-z", NULL"}
array[3] = {"tr", "a-z", "A-Z", NULL"}

だから私は使うことができます

execvp(array[0],array)

後で。

今のところ、すべて問題ないと思います。これらの関数の入出力を相互にリダイレクトしようとすると、問題が発生します。

これが私がやっている方法です:

    mkfifo("queue", 0777);

    for (i = 0; i<= pipelines_count; i++) // eg. if there's 3 pipelines, there's 4 functions to execvp
    {
    int b = fork();             
    if (b == 0) // child
        {           
        int c = fork();

        if (c == 0) 
        // baby (younger than child) 
        // I use c process, to unblock desc_read and desc_writ for b process only
        // nothing executes in here
            {       
            if (i == 0) // 1st pipeline
                {
                int desc_read = open("queue", O_RDONLY);
                // dup2 here, so after closing there's still something that can read from 
                // from desc_read
                dup2(desc_read, 0); 
                close(desc_read);           
                }

            if (i == pipelines_count) // last pipeline
                {
                int desc_write = open("queue", O_WRONLY);
                dup2(desc_write, 0);
                close(desc_write);                              
                }

            if (i > 0 && i < pipelines_count) // pipeline somewhere inside
                {
                int desc_read = open("queue", O_RDONLY);
                int desc_write = open("queue", O_WRONLY);
                dup2(desc_write, 1);
                dup2(desc_read, 0);
                close(desc_write);
                close(desc_read);
                }               
            exit(0); // closing every connection between process c and pipeline             
            }
        else
        // b process here
        // in b process, i execvp commands
        {                       
        if (i == 0) // 1st pipeline (changing stdout only)
            {   
            int desc_write = open("queue", O_WRONLY);               
            dup2(desc_write, 1); // changing stdout -> pdesc[1]
            close(desc_write);                  
            }

        if (i == pipelines_count) // last pipeline (changing stdin only)
            {   
            int desc_read = open("queue", O_RDONLY);                                    
            dup2(desc_read, 0); // changing stdin -> pdesc[0]   
            close(desc_read);           
            }

        if (i > 0 && i < pipelines_count) // pipeline somewhere inside
            {               
            int desc_write = open("queue", O_WRONLY);       
            dup2(desc_write, 1); // changing stdout -> pdesc[1]
            int desc_read = open("queue", O_RDONLY);                            
            dup2(desc_read, 0); // changing stdin -> pdesc[0]
            close(desc_write);
            close(desc_read);                               
            }

        wait(NULL); // it wait's until, process c is death                      
        execvp(array[0],array);         
        }
        }
    else // parent (waits for 1 sub command to be finished)
        {       
        wait(NULL);
        }       
    }

ありがとう。

4

4 に答える 4

6

Patryk さん、なぜ fifo を使用しているのですか?さらに、パイプラインの各ステージに同じ fifo を使用しているのですか?

各ステージ間にパイプが必要なようです。したがって、フローは次のようになります。

Shell             ls               tr                tr
-----             ----             ----              ----
pipe(fds);
fork();  
close(fds[0]);    close(fds[1]);
                  dup2(fds[0],0); 
                  pipe(fds);
                  fork();         
                  close(fds[0]);   close(fds[1]);  
                  dup2(fds[1],1);  dup2(fds[0],0);
                  exex(...);       pipe(fds);
                                   fork();     
                                   close(fds[0]);     etc
                                   dup2(fds[1],1);
                                   exex(...);  

フォークされた各シェル (close、dup2、pipe など) で実行されるシーケンスは、(目的のプロセスの名前とパラメーターを取得する) 関数のように見えます。それぞれの呼び出しまでexec、シェルのフォークされたコピーが実行されていることに注意してください。

編集:

パトリック:

Also, is my thinking correct? Shall it work like that? (pseudocode): 
start_fork(ls) -> end_fork(ls) -> start_fork(tr) -> end_fork(tr) -> 
start_fork(tr) -> end_fork(tr) 

start_fork と end_fork の意味がわかりません。ls開始前に完了するまで実行されることを暗示していtrますか? これは、実際には上の図が意味するものではありません。lsシェルは開始する前に完了するのを待ちませんtr。パイプ内のすべてのプロセスを順番に開始し、プロセスが互いにリンクされるようにとstdinを設定します。のと次の。それが dup2 呼び出しが行っていることです。 stdoutstdoutlsstdintrstdouttrstdintr

プロセスが実行される順序はオペレーティング システム (スケジューラ) によって決定されますが、明らかに、tr実行されて空の状態から読み取られる場合stdinは、前のプロセスがパイプに何かを書き込むまで (ブロックするために) 待機する必要があります。から読み取るls前に が完了する可能性は十分にありますが、そうでない可能性もあります。たとえば、チェーンの最初のコマンドが継続的に実行され、途中で出力を生成するものであった場合、パイプラインの 2 番目のコマンドは、パイプに沿って最初に送信されたものを処理するために時々スケジュールされます。trstdin

物事を少し明確にすることを願っています:-)

于 2012-11-11T14:29:42.310 に答える
2

libpipelineを使用する価値があるかもしれません。それはあなたの側のすべての努力を処理し、パイプラインに関数を含めることさえできます.

于 2012-11-11T19:29:55.437 に答える
1

問題は、一度にすべてを実行しようとしていることです。代わりに、より小さなステップに分割してください。

1) 入力を解析してls -al |抜け出します。1a) これから、パイプを作成し、それを stdout に移動して、開始する必要があることがわかりますls -al。次に、パイプを stdin に移動します。もちろん、さらに多くの機能が追加されますが、コードではまだ心配する必要はありません。

2) 次のセグメントを解析して を取得しますtr a-z A-Z |。next-to-spawn コマンドの出力がどこかにパイプされている限り、ステップ 1a に戻ります。

于 2012-11-11T12:50:07.047 に答える