Linux学習の概要(10)IPCメソッド-パイプPIPE

IPC方式

Linux環境では、プロセスのアドレス空間は互いに独立しています。各プロセスには異なるユーザーアドレス空間があります。どのプロセスのグローバル変数も別のプロセスでは見ることができないため、プロセスとプロセスは互いにアクセスできず、データを交換します。カーネルを介してカーネル内でバッファを開く必要があります。プロセス1はデータをユーザー空間からカーネルバッファにコピーし、プロセス2はカーネルバッファからデータを読み取ります。カーネルによって提供されるこのメカニズムは、相互になります。プロセス通信(IPC)。

ここに画像の説明を挿入
プロセス間のデータ転送を完了するには、ファイル、パイプ、シグナル、共有メモリ、ソケット、名前付きパイプなど、オペレーティングシステムによって提供される特別なメソッドが必要です。コンピュータの急速な発展に伴い、いくつかの方法は、独自の設計上の欠陥のために排除または放棄されました。今日一般的に使用されているプロセス間通信方法は次のとおりです。

  • パイプ(最も使いやすい)
  • 信号(最小オーバーヘッド)
  • 共有マッピングエリア(血縁関係なし)
  • ローカルソケット(最も安定)

パイプライン

パイプは最も基本的なIPCメカニズムの1つであり、血液関連のプロセス間で機能してデータ転送を完了します。パイプは、パイプシステム関数を呼び出すことで作成できます。パイプラインの特徴は次のとおりです。

  • パイプは本質的に疑似ファイル(実際にはカーネルバッファー)です
  • 1つは読み取り側用、もう1つは書き込み側用の2つのファイル記述子によって参照されます。
  • データがパイプの書き込み端からパイプに流れ、読み取り端から流出することを指定します

パイプラインの原理:パイプラインは実際にはカーネルのリングキューメカニズムを使用します。これはカーネルバッファー(4k)の助けを借りて実現されます。
パイプラインの制限:

  • データを単独で書き込むことはできません
  • データが読み取られると、パイプラインに存在せず、繰り返し読み取ることはできません
  • パイプラインは半二重通信を使用するため、データは一方向にしか流れることができません
  • 共通の祖先を持つプロセス間でのみパイプを使用してください

一般的な通信方法は、単方向通信(ブロードキャスト)、半二重通信(WeChatを介して音声を送信)、および全二重通信(電話)です。

パイプ機能

パイプラインを作成する

int pipe(int pipefd [2]);
成功した場合は0を返し、失敗した場合は-1を返し、errnoを設定します

パイプ関数は2つのファイル記述子r / wを正常に返します。開く必要はありませんが、手動で閉じる必要があります。規定:

fd [0]→r読み取り終了
fd [1]→w書き込み終了

0が標準入力に対応し、1が標準出力に対応するのと同様に、パイプラインファイルへのデータの読み取りと書き込みは、カーネルバッファの読み取りと書き込みをリアルタイムで行います
パイプラインが正常に作成された後、パイプラインを作成したプロセス(親プロセス)がパイプラインの読み取りと書き込みの終了を同時に制御します。親プロセスと子プロセス間の通信を実現するにはどうすればよいですか?通常、次の手順を実行でき
ここに画像の説明を挿入
ます。1。親プロセスがpipe関数を呼び出してパイプを作成し、パイプの読み取り端と書き込み端を指す2つのファイル記述子fd [0]とfu [1]を取得します。
2.親プロセスがforkを呼び出して子プロセスを作成すると、子プロセスにも同じパイプを指す2つのファイル記述子があります。
3.親プロセスはパイプの読み取り端を閉じ、子プロセスはパイプの書き込み端を閉じ、親プロセスはパイプにデータを書き込むことができ、子プロセスはパイプ内のデータを読み取ります。パイプラインは循環キューを使用して実現されるため、データは書き込み側からパイプラインに流入し、読み取り側から流出し、プロセス間通信を実現します。

パイプラインの読み取りと書き込みの動作

パイプを使用する場合は、次の4つの特殊なケースに注意する必要があります(パイプがすべてI / O操作をブロックしており、O_NONBLOCKフラグが設定されていない場合)

  • パイプの書き込み端を指すすべてのファイル記述子が閉じていて(パイプの書き込み端の参照カウントが0である)、パイプの読み取り端からデータを読み取るプロセスがまだある場合、残りのデータの後にパイプが読み取られた場合、監視ドキュメントの最後にあるように、再度読み取ると0が返されます。
  • パイプの書き込み端を指すファイル記述子が閉じられておらず(パイプの書き込み端の参照カウントが0より大きい)、パイプの書き込み端を保持しているプロセスがパイプにデータを書き込まない場合、次に、パイプの読み取り端からデータを読み取るプロセスがあり、次にパイプタイプがあります。残りのデータが読み取られた後、読み取りは再びブロックされ、パイプラインが読み取り可能になるまでデータは読み取られず、返されません。
  • パイプの読み取り端を指すすべてのファイル記述子が閉じている場合(パイプの読み取り端の参照カウントが0の場合)、プロセスはパイプの書き込み端に書き込みます。プロセスはSIGPIPEになります。通常、プロセスは異常終了します。もちろん、プロセスを終了せずにSIGPIPE信号をキャプチャすることもできます。
  • パイプの読み取り端を指すファイル記述子が閉じられておらず(パイプの読み取り端の参照テクノロジが0より大きい)、パイプの読み取り端を保持するプロセスがパイプからデータを読み取らない場合、次に、プロセスはパイプの書き込み端にデータを書き込み、次にパイプにデータを書き込みます。パイプがいっぱいになると、書き込みは再びブロックされ、パイプラインに空の位置ができるまでデータを書き込んで戻りません。

概要:
①パイプラインの
読み取り1。パイプラインにデータがあり、読み取りは実際に読み取られたバイト数を返します。
2.パイプラインにデータがありません:
    (1)パイプラインの書き込み端が閉じられ、読み取りが0を返します(ファイルの最後まで読み取るかのように)
    (2)書き込み端がすべて閉じられていないため、読み取りがブロックされます待機(データは近い将来到着する可能性があり、CPUはこの時点で解放されます)
②パイプラインへの書き込み:
1。パイプラインの読み取り端がすべて閉じられ、プロセスが異常終了します(SIGPIPE信号を使用してプロセスを終了させないでください)
2。パイプラインの読み取り端がすべて閉じられているわけではありません。
   (1)パイプラインがいっぱいで、書き込みがブロックされています。
   (2)パイプラインがいっぱいではない場合、writeはデータを書き込み、実際に書き込まれたバイト数を返します。

演習:パイプを使用して、親プロセスと子プロセス間の通信を実現します。完了:ls | wc-l。親プロセスがlsを実装し、子プロセスがwcを実装するとします。
lsコマンドは通常、結果セットをstdoutに書き込みますが、パイプの書き込み側に書き込みます。wc-lは通常、stdinからデータを読み取りますが、パイプの読み取り側から読み取ります。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    
    
	pid_t pid;
	int fd[2];

	pipe(fd);
	pid = fork();

	if (pid == 0) {
    
      //child
		close(fd[1]);	                //子进程从管道中读数据,关闭写端
		dup2(fd[0], STDIN_FILENO);		//让wc从管道中读取数据
		execlp("wc", "wc", "-l", NULL);	//wc命令默认从标准读入取数据

	} else {
    
    

		close(fd[0]);	//父进程向管道中写数据,关闭读端
		dup2(fd[1], STDOUT_FILENO);		//将ls的结果写入管道中
		execlp("ls", "ls", NULL);		//ls输出结果默认对应屏幕
	}

	return 0;
}

プログラムを実行すると、プログラムの実行が終了し、シェルがユーザー入力の待機をブロックしていることがわかります。これは、シェル→フォーク→。/ pipe1、プログラムpipe1の子プロセスがstdinをにリダイレクトするためです。パイプ、および親プロセスによって実行されたlsは、結果セットを通過します。パイプラインは子プロセスに書き込まれます。親プロセスがシェルによって呼び出され、子プロセスがwcの結果を画面に出力する前に待機を呼び出す場合、シェルは最初に$プロンプトを出力します。

演習:パイプを使用して、兄弟プロセス間で通信します。ブラザー:lsブラザー:wc -lファーザー:子プロセスのリサイクルを待っています。
要件:サイクルを使用してN個の子プロセスモデルを作成し、兄弟プロセスを作成し、サイクルシルバーiロゴを使用します

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void)
{
    
    
	pid_t pid;
	int fd[2], i;
	
	pipe(fd);

	for (i = 0; i < 2; i++) {
    
    
		if((pid = fork()) == 0) {
    
    
			break;
        }
    }

	if (i == 0) {
    
    			//兄
		close(fd[0]);				//写,关闭读端
		dup2(fd[1], STDOUT_FILENO);		
		execlp("ls", "ls", NULL);	
	} else if (i == 1) {
    
    	//弟
		close(fd[1]);				//读,关闭写端
		dup2(fd[0], STDIN_FILENO);		
		execlp("wc", "wc", "-l", NULL);		
	} else {
    
    
        close(fd[0]);
        close(fd[1]);
		for(i = 0; i < 2; i++)		//两个儿子wait两次
			wait(NULL);
	}

	return 0;
}

テスト:許可されていますか?パイプには1つの書き込み端と複数の読み取り端がありますか?1つの読み取り終了と複数の書き込み終了を持つことは許可されていますか?

  • 1回の読み取りと複数回の書き込みがハングします
  • 1回の書き込みと複数回の読み取りがハングします

ハング、意味を停止

パイプバッファサイズ

ulimit -aコマンドを使用して、現在のシステムで作成されたパイプファイルに対応するカーネルバッファサイズを表示します。通常:
ここに画像の説明を挿入

パイプラインの長所と短所

利点:

  • シンプル:シグナルと比較して、ソケットはプロセス間通信を実現します。これははるかにシンプルです。

短所:

  • 一方向の通信のみが可能であり、双方向の通信は2つのチャネルを確立する必要があります
  • これは、父と息子の間の通信と兄弟プロセス(共通の祖先を持つ)にのみ使用できます。この問題は後でFIFOの有名なパイプを使用して解決されました

おすすめ

転載: blog.csdn.net/bureau123/article/details/112213307