プロセス間通信 - パイプ通信

目次

 1 パイプラインの概念

 2 名前のないパイプ (パイプ) は、関連するプロセスとのみ通信できます。

ステップ

予防 

3 ウェルノウン パイプ (fifo) は、任意のスタンドアロン プロセスと通信できます。

ステップ

予防


 

1 パイプラインの概念

パイプは UNIX システム上の IPC の最も古い形式であり、すべての UNIX システムがこの通信メカニズムを提供します。パイプには
2 つの制限があります。
1.歴史的に、パイプは半二重でした (つまり、データは一方向にのみ流れることができます)。現在、一部のシステムは全二重パイプを提供していますが、移植性を最大限に高めるために、システムがこの機能を使用することを前提とすべきではありません。
2 つ目は、共通の祖先を持つプロセス間でのみ使用できることです。通常、パイプラインはプロセスによって作成され、
プロセスは fork を呼び出します。その後、パイプラインは親プロセスと子プロセスの間で使用できるようになります。

これら 2 つの制限にもかかわらず、半二重パイプは IPC の最も一般的に使用される形式です。

パイプラインの特徴:
    1. パイプラインは半二重動作モードです
    2. すべてのパイプラインは、位置決め操作をサポートしない特殊なファイルです。
    3. パイプラインは特殊なファイルであり、ファイル IO は読み取りと書き込みに使用されます。(開く、読み取る、書き込む、閉じる)

 2 名前のないパイプ (パイプ) は、関連するプロセスとのみ通信できます。

無名パイプ: サイズ 64k
関数インターフェース:
int Pipe(int Pipefd[2]);
機能:
通信用の無名パイプを作成 (カーネル内)
パラメータ:
Pipefd: ファイル記述子配列空間の最初のアドレスを格納
Pipefd[0]:パイプ ファイル記述子の読み取り
Pipefd[1]: パイプ ファイル記述子の書き込み
戻り値:
成功した場合は 0 を返し 
、失敗した場合は -1 を返します。 

ステップ

パイプラインを作成する == "読み取りおよび書き込みパイプライン == パイプラインを閉じる 

予防 

パイプラインに少なくとも 1 つの書き込みエンドがあります:
1. パイプラインにデータがある場合は、直接読み取ります 
2. パイプラインにデータがない場合は、データが書き込まれるまでブロックして待機し、その後データを読み取ります
。パイプラインに書き込みエンドがない場合:
1. パイプラインの場合 パイプラインにデータがある場合は、直接読み取ります。
2. パイプラインにデータがない場合は、ブロックせずに待機し、
少なくとも 1 つの読み取りエンドに直接戻ります。パイプライン:
1. パイプラインにデータを書き込みます。データがいっぱいでない場合は、直接書き込みます。
2. フル書き込み (64k) の場合は、パイプラインへの書き込みを続行する前に、ブロックしてデータが読み取られるのを待ちます
。読み取り終了はありません
1. パイプラインにデータを書き込むとパイプライン中断エラーが発生する

 コード例を通じてこの名前のないパイプを分析してみましょう

                親子プロセスは、名前のないパイプラインを使用してファイルを転送します。ここでは、パイプライン送信を使用して、画像 src.jpg を生成し、dst.jpg にコピーします。

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>


int main(int argc, char const *argv[])
{
     pid_t pid;
     int pipefd[2],ret;
     ret = pipe(pipefd);
     if(ret < 0)
     {
          perror("fail to pipe");
          return -1;
     }
     pid = fork();

     if(pid > 0)//父进程写
     {
          close(pipefd[0]);
          int fd;
          ssize_t nret;
          fd = open("./src.jpg",O_RDONLY);
          char tmpbuf[1024] = {0};
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          } 
          while (1)
          {
               nret = read(fd,tmpbuf,sizeof(tmpbuf));
               if(0 >= nret)break;
               write(pipefd[1],tmpbuf,nret);
          }
          
          close(pipefd[1]);
          close(fd);
          wait(NULL); 
     }
     else if(0 == pid)//子进程读
     {
          close(pipefd[1]);
          int fd;
          ssize_t nret;
          fd = open("./dst.jpg",O_WRONLY | O_CREAT | O_TRUNC,0664);
          if(-1 == fd)
          {
               perror("fail open");
               return -1;
          }
          char tmp[1024] = {0};
          
          while (1)
          {
               nret = read(pipefd[0],tmp,sizeof(tmp));
               if(0 >= nret)break;
               write(fd,tmp,nret);
          }
          
          close(pipefd[0]);
          close(fd);
          exit(0);
     }
     else
     {    
          perror("fail to fork");
     }
     return 0;
}

これは file io の操作に非常に似ていることがわかりますが、パイプラインの機能を使用して 2 つの親プロセスと子プロセスの間で通信します。

コードを実行すると、src と dst のサイズが 706103 とまったく同じであることがわかります。

 言うまでもなく、写真は同じです

 

 

 

3 ウェルノウン パイプ (fifo) は、任意のスタンドアロン プロセスと通信できます。

関数インターフェース:
int mkfifo(const char *pathname, mode_t mode);
関数:
名前付きパイプの作成
パラメータ:
pathname: 名前付きパイプのパス
mode: 名前付きパイプの権限
戻り値:
成功した場合は 0 を返し 、
失敗した場合は -1 を返します。 

ステップ

1. 作成: mkfifo()

2. 有名なパイプopen()を開きます

3. パイプラインの読み取りと書き込み:ファイル IO

    读: read(fd-read,buff,sizeof(buff));
    写:write(fd-write,buff,sizeof(buff));

4. パイプラインを閉じます: close(fd);

5. パイプラインをアンインストールします:remove();

予防

ウェルノウン パイプは、下方向への実行を続行する前に、両端で読み取りと書き込みを行う必要があります。そうでない場合、
読み取り専用または書き込み専用モードで開かれると、ブロックされます (もう一方の接続が参加するのを待機します)。 。

O_RDONLR でオープンすると、パイプがオープンでブロックされます
。 O_WRONLY でオープンすると、パイプがオープンでブロックされます。

ブロックは、両端が同時に開いている場合にのみ解除されます。

ただし、ターミナル上でチャットするための既知のパイプを使用するには、2 つの異なる .c ファイルを使用します ( 2 つの c ファイル、A_B.c はプロセス ナレッジによって書き込まれ、B_A.c はスレッド ナレッジによって書き込まれます)。

A_B.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>

int main(int argc, char const *argv[])
{
     pid_t pid;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pid = fork();
     if (pid > 0) // 父进程a->b(A发送)
     {
          char tmpbuf[1024] = {0};
          int fa_b = open("./A_B", O_WRONLY); // 打开管道文件(写)
          while (1)
          {
               fgets(tmpbuf, sizeof(tmpbuf), stdin);
               if (!strcmp("quit\n", tmpbuf))break;
               write(fa_b, tmpbuf, strlen(tmpbuf) + 1);
          }
          close(fa_b);
          remove("A_B");
          return 0;
     }
     else if (0 == pid) // 子进程b->a(A接收)
     {
          char tmpbuf[1024] = {0};
          int fb_a = open("./B_A", O_RDONLY); // 打开管道文件(读)
          while (1)
          {
               int nret = read(fb_a, tmpbuf, sizeof(tmpbuf));
               if (!strcmp("quit\n", tmpbuf) || nret <= 0)break;
               printf("\33[32mB->A\33[0m: %s", tmpbuf);
          }
          close(fb_a);
          remove("./B_A");
          return 0;
     }
     else
     {
          perror("fail to fork");
          return 0;
     }
}

 B_A.c

#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>
#include <semaphore.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
void *A_B(void *arg) // a->b(B接收)
{
     char tmpbuf[1024] = {0};
     int fa_b = open("./A_B", O_RDONLY); // 打开管道文件(读)
     while (1)
     {
          int nret = read(fa_b, tmpbuf, sizeof(tmpbuf));
          if (!strcmp("quit\n", tmpbuf))break;
          printf("\33[33mA->B\33[0m: %s", tmpbuf);
     }
     close(fa_b);
     remove("./A_B");
     exit(0);
}
void *B_A(void *arg) // b->a(B发送)
{
     char tmpbuf[1024] = {0};
     int fb_a = open("./B_A", O_WRONLY); // 打开管道文件(写)
     while (1)
     {
          fgets(tmpbuf, sizeof(tmpbuf), stdin);
          if (!strcmp("quit\n", tmpbuf))break;
          write(fb_a, tmpbuf, strlen(tmpbuf) + 1);
     }
     close(fb_a);
     remove("B_A");
     exit(0);
}
int main(int argc, char const *argv[])
{
     pthread_t tid1, tid2;
     int ret1 = mkfifo("./A_B", 0664); // 创建有名管道
     int ret2 = mkfifo("./B_A", 0664); // 创建有名管道
     if ((-1 == ret1 || -1 == ret2) && errno != EEXIST)
     {
          perror("fail to mkfifo");
          return -2;
     }
     pthread_create(&tid1, NULL, A_B, NULL);
     pthread_create(&tid2, NULL, B_A, NULL);

     pthread_join(tid1, NULL);
     pthread_join(tid2, NULL);
     return 0;
}

このようにして、2 つのコードを同時に実行すると、図に示すように、半二重送受信通信用の 2 つのチャネルが作成されます (テキストが欠落しているのは、半角と全角が異なるバグによるものです)中国語はターミナルで削除されます)。

 

 

おすすめ

転載: blog.csdn.net/m0_58193842/article/details/128554609
おすすめ