パイプの処理

パイプは 2 つのプロセスが通信するために作成される名前なしオブジェクトです。

1 つのプロセスが読み取り、他のプロセスがパイプ・ファイルに書き込みます。 この特殊なタイプのファイルは先入れ先出し (FIFO) ファイルとも呼ばれます。 FIFO のデータ・ブロックは循環キューで操作され、 データの FIFO の順序を保持するために、 内部で読み取りポインターと書き込みポインターを保守します。 limits.h ファイルに定義されている PIPE_BUF システム変数は、パイプへの書き込み時に必ずアトミックになる最大バイト数を指定します。

シェルは名前なしパイプを使用して、コマンドのパイプライン処理を実行します。 大部分の名前なしパイプはシェルによって作成されます。 | (垂直) シンボルは、 プロセス間のパイプを表します。 次の例では、ls コマンドの出力が画面に表示されます。
ls | pr

パイプは、可能であれば、通常のファイルとして処理されます。 通常は、現在のオフセット情報はシステム・ファイル・テーブルに保管されます。 ただし、 パイプは複数のプロセスによって共用されるので、読み取り/書き込みポインターは、 プロセスにではなく、ファイルに固有でなければなりません。 ファイル・テーブル・エントリーは open サブルーチンによって作成されるので、ファイルにではなく、 オープン・プロセスに固有です。 パイプにアクセスするプロセスは、 共通のシステム・ファイル・テーブル・エントリーを介して、アクセスを共用します。

pipe サブルーチンの使用

pipe サブルーチンは、プロセス間チャネルを作成し、 2 つのファイル・ディスクリプターを戻します。 ファイル・ディスクリプター 0 は読み取り用にオープンされます。 ファイル・ディスクリプター 1 は書き込み用にオープンされます。 読み取り操作は FIFO ベースでデータにアクセスします。 これらのファイル・ディスクリプターは、readwrite、および close の各サブルーチンで使用されます。

次の例では、子プロセスが作成され、 パイプを介してそのプロセス ID を戻しています。
#include <sys/types.h>
main()
{
        int p[2];
        char buf[80];
        pid_t pid;

        if (pipe(p))
        {
                  perror("pipe failed");
                exit(1)'
        }
        if ((pid=fork()) == 0)
        {
                                       /* in child process */
                close(p[0]);           /*close unused read */
                                       *side of the pipe */
                sprintf(buf,"%d",getpid()); 
                                       /*construct data */
                                       /*to send */
                write(p[1],buf,strlen(buf)+1);
                        /*write it out, including
                        /*null byte */
                exit(0);
        }
                                        /*in parent process*/
        close(p[1]);                    /*close unused write side of pipe */
        read(p[0],buf,sizeof(buf));     /*read the pipe*/
        printf("Child process said: %s/n", buf);
                                       /*display the result */
        exit(0);
}

プロセスは、空のパイプを読み取ると、データが到着するまで待ちます。 プロセスは、満杯のパイプに書き込むと (PIPE_BUF)、 スペースが使用可能になるまで待ちます。 パイプの書き込みサイドがクローズすると、 このパイプへの以後の読み取り操作によって、ファイルの終わりが戻されます。

パイプを制御するその他のサブルーチンは popenpclose です。
popen
パイプを作成してから (pipe サブルーチンを使用して)、 コール側のコピーを作成するために fork します。 子プロセスは、読み取りか書き込みかを判断し、パイプの他のサイドをクローズしてから、シェルを呼び出して (execl サブルーチンを使用して)、必要なプロセスを実行します。

親は、パイプの使用しなかったサイドをクローズします。 このクローズは、 ファイル終わりのテストを正しく行うために必要です。 例えば、 パイプを読み取ることになっている子プロセスがパイプの書き込みサイドをクローズしない場合、 アクティブである可能性のある書き込みプロセスが 1 つあるので、 その子プロセスはそのパイプでファイルの終わり条件を検出することはありません。

パイプ・ディスクリプターをプロセスの標準入力に関連付ける標準的な方法を次に示します。

close(p[1]);
close(0);
dup(p[0]);
close(p[0]);

close サブルーチンは、 ファイル・ディスクリプター 0 の標準入力を切断します。 dup サブルーチンは、 既にオープンされたファイル・ディスクリプターの複製を戻します。 ファイル・ディスクリプターは昇順で割り当てられ、 最初に使用可能なファイル・ディスクリプターが戻されます。 dup サブルーチンは、 パイプ (読み取りサイド) のファイル・ディスクリプターをファイル・ディスクリプター 0 にコピーするので、 標準入力がパイプの読み取りサイドになります。 最後に、以前の読み取りサイドがクローズされます。 このプロセスは、子プロセスの親からの書き込みに似ています。

pclose
コール側のプログラムと、実行するシェル・コマンドの間のパイプをクローズします。 pclose サブルーチンを使用して、 popen サブルーチンによってオープンしたすべてのストリームをクローズします。

pclose サブルーチンは、 関連するプロセスが終了するのを待ってから、 クローズし、コマンドの終了状況を戻します。 pclose サブルーチンは、 子プロセスが終了するのを待ってからパイプをクローズするので、close サブルーチンより望ましいものとなります。 同様に重要な点として、プロセスが複数の子プロセスを作成した場合、 一部の子プロセスが作業を完了したとしても、存在できる終了しない子プロセスの数に制限があります。 待つことによって、各子プロセスはその作業を完了することができます。