🛠パイプのソースコード ソース千夜一夜

シェルは、ユーザーからのコマンドを受け取り、それを解析し、対応するシステムコールをオペレーティングシステムに送信する役割を果たします。たとえば、UNIX系のシステムでは、bash シェルが広く使われています。bash シェルのソースコードは、bash プロジェクトの公式リポジトリで公開されています。
パイプ機能は、シェルが複数のコマンドを連結し、それらのコマンド間でデータをやり取りするために使います。たとえば、command1 | command2 というコマンドを実行すると、command1 の標準出力を、command2 の標準入力として渡します。

この機能は、シェルのソースコードにおいて、コマンドの解析や実行を行う部分で実装されています。具体的なコードの構造は、そのシェルの実装によりますが、通常、以下の手順で行われます。

  1. シェルは、ユーザーからの入力を解析します。

  2. パイプ | があれば、それを検出し、パイプを作成します。

  3. パイプの両端を、それぞれのコマンドの標準入力・標準出力に接続します。

  4. それぞれのコマンドを実行します。

bashのソースコードはかなり大きく、パイプ処理は複数の箇所で行われていますが、主にexecute_cmd.cというファイルにその処理の一部が含まれています。

https://github.com/bminor/bash/blob/master/execute_cmd.c

このファイルには、コマンドの実行に関連するいくつかの関数が含まれており、それらの関数はコマンドラインを解析し、コマンドを実行する役割を果たします。

たとえば、execute_pipeline関数は、パイプライン(つまり、|で連結された複数のコマンド)を実行するための関数です。この関数は、パイプを作成し、それぞれのコマンドを別々のプロセスとして実行し、パイプの両端を適切なファイルディスクリプタに接続します。

https://github.com/bminor/bash/blob/master/execute_cmd.c#L2496


execute_pipelineは、コマンドパイプラインを実行するためのものです。

以下に、解説を行っていきます。

  1. 関数の引数:

    • COMMAND *command; - 実行すべきコマンドやコマンドのシーケンスを表す構造体。

    • int asynchronous; - コマンドが非同期で実行されるべきかどうかを示すフラグ。

    • int pipe_in, pipe_out; - パイプラインの入力と出力のファイルディスクリプタを表します。

    • struct fd_bitmap *fds_to_close; - 閉じるべきファイルディスクリプタのリストを含む構造体。

  2. ローカル変数:

    • いくつかのローカル変数が宣言されていますが、これらはパイプラインの実行やジョブ制御、ファイルディスクリプタの管理などのためのものです。

  3. #if defined (JOB_CONTROL):

    • ジョブ制御が有効な場合、子プロセスのシグナルをブロックするためのコードの開始を示します。

  4. ignore_return = (command->flags & CMD_IGNORE_RETURN) != 0;:

    • コマンドの終了ステータスを無視するかどうかを確認します。

  5. stdin_valid = sh_validfd (0);:

    • 標準入力が有効なファイルディスクリプタであるかどうかをチェックします。

  6. whileループ:

    • このループは、パイプライン中の各コマンドを順番に処理します。

    • コマンドが'|'(パイプ)で結ばれている場合、ループは続行されます。

  7. if (pipe (fildes) < 0):

    • 新しいパイプを作成します。fildesは配列で、fildes[0]は読み取り専用のファイルディスクリプタ、fildes[1]は書き込み専用のファイルディスクリプタを保持します。

/* ここに問題があります:新しいファイルのclose-on-execコードにより、 パイプの読み取り端 (fildes[0]) が最初のプロセスで開いたままになっています。 そのため、そのプロセスはSIGPIPEを受け取ることがありません。 フォーク後にfildes[0]を閉じるように最初のプロセスにシグナルを送る方法はありませんので、 それは開いたままとなります。パイプに接続された読み取り用のファイルディスクリプタが まだ開いているため、SIGPIPEは送信されません。ここでそれを対処しています。 これは、execute_simple_commandで子プロセスを作成した後に閉じる必要がある ファイルディスクリプタのビットマップを渡します。*/ /* fd_bitmapは少なくともfildes[0]と同じ大きさである必要があります。 もしfildes[0]がfds_to_close->sizeより小さい場合は、 fds_to_close->sizeを使用します。*/


お願い致します