Linux では、パイプは、あるプログラムの出力を別のプログラムの入力に直接接続する通信メカニズムです。本質的に、パイプもファイルの一種ですが、一般的なファイルとは異なります。パイプを使用すると、通信にファイルを使用する際の 2 つの問題を克服できます。具体的には、パイプのサイズを制限し、読み取りプロセスが機能する可能性があります。よりも速い 書き込みプロセスが高速です。
#このチュートリアルの動作環境: linux7.3 システム、Dell G3 コンピューター。
パイプラインは Linux における非常に重要な通信方法であり、あるプログラムの出力を別のプログラムの入力に直接接続します。パイプは、多くの場合、名前のないパイプを指します。名前のないパイプは、関連するプロセス間でのみ使用できます。これが、名前付きパイプとの最大の違いです。
有名なパイプは名前付きパイプまたは FIFO (先入れ先出し) と呼ばれ、関数 mkfifo() で作成できます。
Linux パイプ実装メカニズム
Linux では、パイプは非常に頻繁に使用される通信メカニズムです。本質的に、パイプもファイルの一種ですが、一般的なファイルとは異なります。パイプを使用すると、通信にファイルを使用する際の 2 つの問題を解決できます。具体的なパフォーマンスは次のとおりです。
パイプラインのサイズを制限します。事実上、パイプは固定サイズのバッファーです。 Linux では、このバッファのサイズは 1 ページ、つまり 4K バイトであるため、ファイルのようにサイズが無制限に増大することはありません。単一の固定バッファを使用すると、書き込み中にパイプがいっぱいになるなどの問題が発生する可能性があります。これが発生すると、パイプへの後続の write() 呼び出しはデフォルトでブロックされ、データが読み取られるのを待機するため、十分なスペースが確保されます。書き込みのための write() 呼び出しの場合。
読み取りプロセスは、書き込みプロセスよりも高速に動作する場合もあります。現在のプロセス データがすべて読み取られると、パイプは空になります。これが発生すると、後続の read() 呼び出しはデフォルトでブロックされ、データが書き込まれるのを待機します。これにより、ファイルの終わりを返す read() 呼び出しの問題が解決されます。
注: パイプからのデータの読み取りは 1 回限りの操作です。データが読み取られると、データはさらにデータを書き込むためのスペースを確保するためにパイプから破棄されます。
1. パイプラインの構造
Linux では、パイプラインの実装は特別なデータ構造を使用せず、ファイルのファイル構造に依存します。システムと VFS インデックス ノードの i ノード。これは、2 つのファイル構造を同じ一時 VFS インデックス ノードにポイントし、さらに物理ページをポイントすることによって実現されます。
2. パイプの読み取りと書き込み
パイプライン実装のソース コードは fs/pipe.c にあります。pipe.c には、次の 2 つを含む多くの関数があります。より重要な関数は、パイプ読み取り関数 Pipe_read() とパイプ書き込み関数 Pipe_wrtie() です。パイプ書き込み関数は、VFS インデックス ノードが指す物理メモリにバイトをコピーすることによってデータを書き込みます。一方、パイプ読み取り関数は、物理メモリ内のバイトをコピーすることによってデータを読み取ります。もちろん、カーネルはパイプへのアクセスを同期するために、ロック、待機キュー、およびシグナルを使用する特定のメカニズムを使用する必要があります。
書き込みプロセスがパイプに書き込むとき、標準ライブラリ関数 write() を使用します。システムは、ライブラリ関数によって渡されたファイル記述子に基づいてファイルのファイル構造を見つけることができます。ファイル構造は書き込み操作の実行に使用される関数 (つまり書き込み関数) のアドレスを指定するため、カーネルはこの関数を呼び出して書き込み操作を完了します。書き込み関数がデータをメモリに書き込む前に、まず VFS インデックス ノードの情報を確認する必要があります。次の条件が満たされると、実際のメモリ コピー作業を実行できます:
メモリ内には、書き込むすべてのデータを収容するのに十分なスペースがあります。
メモリはリーダーによってロックされていません。
上記の条件が同時に満たされた場合、書き込み関数はまずメモリをロックし、次に書き込みプロセスのアドレス空間からメモリにデータをコピーします。それ以外の場合、書き込みプロセスは VFS インデックス ノードの待機キューでスリープし、次にカーネルがスケジューラを呼び出し、スケジューラは実行する他のプロセスを選択します。書き込みプロセスは実際には割り込み可能な待機状態にあります。書き込まれたデータを収容するのに十分なスペースがメモリにある場合、またはメモリのロックが解除されている場合、読み取りプロセスは書き込みプロセスを起動します。このとき、書き込みプロセスはシグナル。データがメモリに書き込まれた後、メモリのロックが解除され、インデックス ノード上でスリープしているすべての読み取りプロセスが起動されます。
パイプの読み取りプロセスは、書き込みプロセスと似ています。ただし、データがない場合、またはメモリがロックされている場合、ファイルまたはパイプのオープン モードに応じて、プロセスはプロセスをブロックするのではなく、ただちにエラー メッセージを返すことができます。逆に、プロセスはインデックス ノードの待機キューでスリープし、書き込みプロセスによるデータの書き込みを待つことができます。すべてのプロセスがパイプ操作を完了すると、パイプの i ノードが破棄され、共有データ ページが解放されます。
パイプラインの実装には多くのファイルの操作が含まれるため、読者がファイル システムについて学び終えてから、pipe.c のコードを読むと、理解するのは難しくないことがわかります。
Linux パイプは、必要なパラメータが少ないため、作成と使用が簡単です。 Windows、Linux、UNIX と同じパイプ作成目標を達成するには、次のコード スニペットを使用します。
Linux 名前付きパイプの作成
int fd1[2]; if(pipe(fd1)) { printf("pipe() FAILED: errno=%d",errno); return 1; }
Linuxパイプ ペア ブロックするまでの書き込み操作のサイズには制限があります。各パイプに特に使用されるカーネル レベルのバッファーは、ちょうど 4096 バイトです。 4K を超える書き込み操作は、リーダーがパイプをクリアしない限りブロックされます。実際には、読み取り操作と書き込み操作は異なるスレッドで実装されるため、これは制限ではありません。
Linux は名前付きパイプもサポートします。これらの数値に関する初期の解説者は、公平性を保つために、Linux の名前付きパイプと Windows の名前付きパイプを比較するべきだと提案しました。 Linux で名前付きパイプを使用する別のプログラムを作成しました。 Linux では、名前付きパイプと名前なしパイプの結果に違いがないことがわかりました。
Linux パイプは Windows 2000 名前付きパイプよりもはるかに高速であり、Windows 2000 名前付きパイプは Windows XP 名前付きパイプよりもはるかに高速です。
例:
#include<stdio.h> #include<unistd.h> int main() { int n,fd[2]; // 这里的fd是文件描述符的数组,用于创建管道做准备的 pid_t pid; char line[100]; if(pipe(fd)<0) // 创建管道 printf("pipe create error/n"); if((pid=fork())<0) //利用fork()创建新进程 printf("fork error/n"); else if(pid>0){ //这里是父进程,先关闭管道的读出端,然后在管道的写端写入“hello world" close(fd[0]); write(fd[1],"hello word/n",11); } else{ close(fd[1]); //这里是子进程,先关闭管道的写入端,然后在管道的读出端读出数据 n= read(fd[0],line,100); write(STDOUT_FILENO,line,n); } exit(0); }
推奨学習: Linux ビデオ チュートリアル
以上がLinuxパイプラインとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。