1

宿題用に独自のシェルを作成していて、問題が発生しています。

私のシェル プログラムはcat scores | grep 100コンソールから入力を取得し、期待どおりに出力を出力しますが、grepコマンドは終了せず、コマンドから無限に実行されていることがわかりますps


編集 - fds のクローズ中にエラーが発生しました。現在、grep コマンドは実行されておらず、コンソール出力は -

grep: (標準入力): 不正なファイル記述子


int array fd[][]最初のプロセスを分岐する前に、コンソールからコマンドの数を読み取り、必要なパイプを作成して 2 次元に格納しています。

fd[0][0]最初のパイプの読み取り終了が含まれ、最初のパイプfd[0][1]の書き込み終了が含まれます。fd[1][0]2 番目のパイプの読み取り終了が含まれ、2 番目のパイプfd[1][1]の書き込み終了が含まれます。

新しい各プロセスstdinは、パイプの読み取り側で前のプロセスと複製しstdout、パイプの書き込み側で次のプロセスと複製します。

以下は私の機能です:

void run_cmds(char **args, int count,int pos)
{
    int pid,status;
    pid = fork();
    if ( pid == 0 )
    {
        if(pos != 0) dup2(fd[pos-1][0],0); // not changing stdin for 1st process
        if(pos != count) dup2(fd[pos][1],1); //not changing stdout for last process
        close_fds(pos);
        execvp(*args,args);
    }
    else
    {
        waitpid(pid,&status,0);
        count--;
        pos++;
        //getting next command and storing it in args
        if(count > 0)
            run_cmds(args,count,pos);
        }
    }
}
  • argsには、コマンドの引数が含まれます。
  • countは、作成する必要があるコマンドの数です。
  • posは、入力内のコマンドの位置です

私は問題を理解することができません。これの前に、ハードコードされた値に対して同じアプローチを使用しましたが、機能していました。

/ の理解/実装に欠けているものdup2fork、コマンドが無限に待機しているのはなぜですか?

入力は非常に役立ちます。過去数日間、これに襲われました!


EDIT : close_fds() 関数は以下のとおりです - どのプロセスでも、プロセスをリンクしている両方のパイプを閉じています。

void close_fds(int pos)
{
 if ( pos != 0 )
        {
        close(fd[pos-1][0]);
        close(fd[pos-1][1]);
        }
 if ( pos != count) 
        {
        close(fd[pos][0]);  
        close(fd[pos][1]);
        }
}
4

2 に答える 2

3

最初の診断

あなたは言う:

新しいプロセスはそれぞれ、そのパイプの読み取り側で前のプロセスとの stdin を複製し、次のプロセスでそのパイプの書き込み側でその stdout を複製します。

あなたは魔法の言葉について言及していませんclose()

dup()各パイプを使用するとき、またはdup2()標準入力に接続するときは、各パイプの読み取り側と書き込み側の両方を確実に閉じる必要があります。つまり、2 つのパイプを使用すると、4 つの呼び出しが行われclose()ます。

パイプを正しく閉じないと、読み取り中のプロセスは EOF を取得しません (パイプに書き込む可能性のあるプロセス (おそらくそれ自体) があるため)。を十分に (少なすぎず、多すぎず) 呼び出すことが重要close()です。


close_fds()dup2 呼び出しの後に呼び出しています。fd[][2]関数は配列を通過し、配列内のclose()それぞれに対して呼び出しを行いfdます。

わかった。それは重要です。それは私の最初の診断がおそらく的外れだったことを意味します。

二次診断

他のいくつかの項目:

  1. execvp()の後にエラーを報告し、 が返された場合に終了するコードが必要ですexecvp()(つまり、失敗します)。

  2. すぐに電話しないでくださいwaitpid()。パイプライン内のすべてのプロセスを同時に実行できるようにする必要があります。すべてのプロセスを起動し、最後のプロセスが終了するのを待ち、終了したプロセスをクリーンアップする必要があります (ただし、続行する前にパイプライン内のすべてのプロセスが終了することを心配する必要はありません)。

    2 番目のコマンドを起動する前に最初のコマンド全体を強制的に実行し、最初のコマンドがパイプに収まらない出力を生成した場合、デッドロックが発生します — 最初のプロセスは、書き込みがブロックされているため終了できません。 、最初のプロセスが終了していないため、2 番目のプロセスを開始できません。中断と再起動、そして宇宙の終わりはすべて、問題をやや大雑把に解決します。

  3. 再帰する前に、デクリメントcountとインクリメントを行います。posそれは悪いかもしれません。増やすだけでいいと思いますpos

三次診断

機能を表示する更新後close_fds()

「パイプのクローズに問題があります」に戻ります (ただし、待機とエラー報告の問題は依然として問題です)。パイプラインに 6 つのプロセスがあり、プロセスが実行される前に 5 つの接続パイプすべてが作成された場合、各プロセスは 10 個のパイプ ファイル記述子すべてを閉じる必要があります。

また、パイプライン内のコマンドの 1 つを実行するサブシェルではなく、親シェルでパイプが作成される場合、親は、コマンドが完了するのを待つ前に、すべてのパイプ記述子を閉じる必要があることを忘れないでください。

MCVE (最小、完全、検証可能な例を作成する方法は? ) または SSCCE (短い、自己完結型、正しい例) を作成してください。同じ基本的なアイデアに対する 2 つの名前とリンクです。

を呼び出すコードに渡すデータ構造を作成するプログラムを作成する必要がありますrun_cmds()cat score | grep 100つまり、解析コードが作成するデータ構造を作成し、' ' コマンドのパイプを作成するコードを表示する必要があります。

再帰がどのように機能するか、またはあなたの例で呼び出されるかどうかは、もはや明確ではありません。実際、あなたの例では使用されていないと思いますが、同じコマンドが複数回実行されることになるため、おそらく同様です.AFAICS.

于 2014-10-11T00:39:36.503 に答える