Unix系OSにおけるパイプ(pipe)とパイプライン(pipeline)
Unix系オペレーティングシステムにおけるパイプとは、複数のプログラムの入出力を接続するための
プロセス間通信の一種です。パイプを使用することで、複数のプログラムを組み合わせて、多様で複雑な
データ処理を効率的かつ柔軟に実行できます。
パイプの概要
パイプを使用する主な利点は以下の通りです。
複雑な処理の効率化: 複数のプログラムを組み合わせることで、複雑な
データ処理を効率的に実行できます。
既存ソフトウエアの再利用: 既存のソフトウェア資産を再利用することで、プログラムの生産性を向上させます。
処理の分割と組み合わせ: 複雑な処理を小さな機能に分割し、それぞれの機能を担当するプログラムを組み合わせて処理を行います。これにより、プログラム全体の複雑さを軽減し、保守性を向上させます。
UNIX系OSとパイプの関係
データ処理は一般的に、入力データ、処理を行うプログラム、そして出力データの3つの要素で構成されます。特に複雑な処理を行う場合、プログラム自体が複雑になり、
バグや保守性の低下を引き起こしやすくなります。また、特定の複雑な処理に特化したプログラムは、再利用性が低く、生産性の観点からも問題があります。
この問題を解決するために、
UNIX系OSでは「1つのことをうまくやる、道具のようなソフトウェア」をパイプラインで組み合わせるという考え方が採用されています。具体的には、複雑な
データ処理を機能ごとに小さく分割し、それぞれの機能に対してシンプルなソフトウェアを作成します。そして、各ソフトウェアの出力を次のソフトウェアの入力にパイプで接続することで、複雑な処理を連携して実現します。
パイプで連結されることを前提としたプログラムをフィルタと呼びます。パイプには、直接親子関係にある
プロセス間で通信を行う「無名パイプ」と、親子関係にない
プロセス間で一時ファイルを用いて通信を行う「名前付きパイプ」があります。
シェルでのパイプの利用
シェルでは、縦棒(|)の記号を使って無名パイプを簡単に利用できます。この記号は、ある
プロセスの標準出力(stdout)を、次の
プロセスの標準入力(stdin)に直接接続します。
シェルのコマンドラインにおけるパイプは、
中置記法で
結合法則を満たす演算子とみなすことができます。例えば、関数 f, g, h があるとき、`h(g(f(x)))` という計算を `(h∘g∘f)(x)` と合成関数で表現するのと同様に、`progF < x | progG | progH` は、各プログラムをパイプで繋ぎ合わせることで一連の処理を表現します。
パイプに関連するシグナル
パイプの読み出し側が閉じられた後に、パイプへの書き込みが行われた場合、書き込もうとした
プロセスには `SIGPIPE` シグナルが送られます。このシグナルを受け取ると、
プロセスは通常異常終了します。これは、パイプの
バッファが枯渇し、書き込み側がブロックされるのを防ぐための仕組みです。
以下は、パイプの典型的な使用例です。
bash
grep 札幌市 Address.txt | a2ps | lpr
この例では、`Address.txt` ファイルから「札幌市」を含む行を `grep` コマンドで抽出し、その結果を `a2ps` コマンドで整形し、最後に `lpr` コマンドで印刷します。
パイプを使用しない場合、中間ファイルを利用する必要があり、記述が冗長になるだけでなく、一般的に処理も遅くなります。パイプを使用することで、複数のプログラムを同時に実行し、I/Oなどの待ち時間を有効に活用できるため、処理を高速化できます。
パイプの利点
処理速度の向上: パイプを使用することで、中間ファイルの書き込みを省略できるため、処理を高速化できます。
メモリ使用量の削減: パイプは逐次処理を行うため、データサイズが大きい場合でもメモリを大量に消費することはありません。
柔軟な組み合わせ: 既存の
UNIXコマンドを自由に組み合わせることで、様々な処理を迅速に実現できます。
生産性の向上: 専用のソフトウェアを作成するよりも、パイプを使用する方が効率的で生産性が高い場合があります。
エラーストリーム
デフォルトでは、パイプ内の
プロセスの標準エラーストリーム(stderr)はパイプを通して渡されず、起動した
コンソールに出力されます。しかし、多くの
シェルでは、標準エラーストリームもパイプに渡すための追加構文が用意されています。
Pipemill
シェルをパイプ処理に直接関与させることも可能です。このような技法を「pipemill」と呼びます。
プログラムによるパイプの作成
`pipe()`
システムコールを使用すると、プログラム内で新しいパイプ(無名パイプ)を作成できます。`fork()`
システムコールと組み合わせて使用することで、親子関係にある
プロセス間で通信を行うことができます。
名前付きパイプ
UNIX'>[POSIX]では、`mkfifo()` または `mknod()` を使用して、ファイルシステムの名前空間に「名前付きパイプ」を作成できます。名前付きパイプを使用することで、親子関係にない任意の
プロセス間でパイプ通信を行うことができます。
パイプの実装
通常、OSはパイプの
バッファリング機能を提供します。これにより、送信側と受信側の処理速度が異なる場合でも、データの損失を防ぐことができます。
ネットワークパイプ
`netcat` などのツールを使用すると、パイプをTCP/IPソケットに接続することもできます。
パイプの歴史
パイプの概念と縦棒(|)による記法は、初期のUnix
シェル開発に関わったダグラス・マキルロイが考案しました。1973年に
ケン・トンプソンが
UNIXにパイプを実装し、その後、多くの
オペレーティングシステムのコマンドライン
シェルに採用されました。
UNIX系以外のOSでのパイプ
MS-DOS: MS-DOSでは、パイプは中間ファイルによってエミュレートされています。
*
Windows: Windowsでは、名前付きパイプと匿名パイプが利用可能です。特に、
プロセス間通信においては、名前付きパイプが高速に動作します。
まとめ
パイプは、
UNIX系OSにおける重要な概念であり、効率的な
データ処理を実現するための強力なツールです。パイプを理解し、適切に活用することで、より柔軟で効率的なシステムを構築できます。