Inter-process-communication-named-pipes

提供:Dev Guides
移動先:案内検索

プロセス間通信-名前付きパイプ

パイプは、関連するプロセス間の通信を目的としていました。 ある端末からクライアントプログラムを実行し、別の端末からサーバープログラムを実行するなど、無関係なプロセス通信にパイプを使用できますか? 答えはノーだ。 次に、無関係なプロセスの通信をどのように達成できるか、簡単な答えは名前付きパイプです。 これは関連するプロセスで機能しますが、関連するプロセスの通信に名前付きパイプを使用する意味はありません。

一方向通信に1本のパイプを使用し、双方向通信に2本のパイプを使用しました。 名前付きパイプにも同じ条件が適用されますか? 答えはノーです。名前付きパイプは双方向通信をサポートしているため、双方向通信(サーバーとクライアント、さらにクライアントとサーバー間の通信)に使用できる単一の名前付きパイプを使用できます。

名前付きパイプの別の名前は* FIFO(先入れ先出し)*です。 名前付きパイプを作成するシステムコール(mknod())を見てみましょう。これは一種の特別なファイルです。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int mknod(const char *pathname, mode_t mode, dev_t dev);

このシステムコールは、通常のファイル、デバイスファイル、FIFOなどの特別なファイルまたはファイルシステムノードを作成します。 システムコールの引数は、パス名、モード、およびdevです。 モード名とデバイス情報のパス名。 パス名は相対パスです。ディレクトリが指定されていない場合、現在のディレクトリに作成されます。 指定されるモードは、次の表に示すように、ファイルのタイプやファイルモードなどのファイルタイプを指定するファイルモードです。 devフィールドは、メジャーおよびマイナーデバイス番号などのデバイス情報を指定するためのものです。

File Type Description File Type Description
S_IFBLK block special S_IFREG Regular file
S_IFCHR character special S_IFDIR Directory
S_IFIFO FIFO special S_IFLNK Symbolic Link
File Mode Description File Mode Description
S_IRWXU Read, write, execute/search by owner S_IWGRP Write permission, group
S_IRUSR Read permission, owner S_IXGRP Execute/search permission, group
S_IWUSR Write permission, owner S_IRWXO Read, write, execute/search by others
S_IXUSR Execute/search permission, owner S_IROTH Read permission, others
S_IRWXG Read, write, execute/search by group S_IWOTH Write permission, others
S_IRGRP Read permission, group S_IXOTH Execute/search permission, others

ファイルモードは、0XYZなどの8進表記でも表すことができます。Xは所有者を表し、Yはグループを表し、Zはその他を表します。 X、Y、またはZの値の範囲は0〜7です。 読み取り、書き込み、実行の値はそれぞれ4、2、1です。 読み取り、書き込み、実行の組み合わせで必要な場合は、それに応じて値を追加します。

たとえば、0640と言えば、所有者には読み取りと書き込み(4 + 2 = 6)、グループには読み取り(4)、その他には許可なし(0)を意味します。

この呼び出しは、成功すると0を返し、失敗すると-1を返します。 失敗の原因を知るには、errno変数またはperror()関数で確認してください。

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode)

このライブラリ関数は、名前付きパイプに使用されるFIFO特殊ファイルを作成します。 この関数の引数は、ファイル名とモードです。 ファイル名は、絶対パスでも相対パスでもかまいません。 フルパス名(または絶対パス)が指定されていない場合、実行プロセスの現在のフォルダーにファイルが作成されます。 ファイルモード情報は、mknod()システムコールで説明されているとおりです。

この呼び出しは、成功すると0を返し、失敗すると-1を返します。 失敗の原因を知るには、errno変数またはperror()関数で確認してください。

ある端末でサーバーを実行し、別の端末でクライアントを実行するプログラムを考えてみましょう。 このプログラムは、一方向の通信のみを実行します。 クライアントはユーザー入力を受け入れ、サーバーにメッセージを送信し、サーバーは出力にメッセージを印刷します。 ユーザーが文字列「end」を入力するまで、プロセスは継続されます。

例でこれを理解しましょう-

  • ステップ1 *-2つのプロセスを作成します。1つはfifoserverで、もう1つはfifoclientです。
  • ステップ2 *-サーバープロセスは以下を実行します-
  • 「MYFIFO」という名前の名前付きパイプを(システムコールmknod()を使用して)作成します(作成されていない場合)。
  • 読み取り専用の名前付きパイプを開きます。
  • ここでは、所有者の読み取りと書き込みの権限を持つFIFOを作成しました。 グループの読み取り、その他のアクセス許可はありません。
  • クライアントからのメッセージを無限に待機します。
  • クライアントから受信したメッセージが「終了」ではない場合、メッセージを出力します。 メッセージが「終了」の場合、FIFOを閉じてプロセスを終了します。
  • ステップ3 *-クライアントプロセスは以下を実行します-
  • 書き込み専用の名前付きパイプを開きます。
  • ユーザーからの文字列を受け入れます。
  • ユーザーが「終了」または「終了」以外を入力したかどうかを確認します。 いずれにせよ、サーバーにメッセージを送信します。 ただし、文字列が「終了」の場合、これによりFIFOが閉じられ、プロセスも終了します。
  • ユーザーが文字列「end」を入力するまで無限に繰り返します。

次に、FIFOサーバーファイルを見てみましょう。

/*Filename: fifoserver.c*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "MYFIFO"
int main() {
   int fd;
   char readbuf[80];
   char end[10];
   int to_end;
   int read_bytes;

  /*Create the FIFO if it does not exist*/
   mknod(FIFO_FILE, S_IFIFO|0640, 0);
   strcpy(end, "end");
   while(1) {
      fd = open(FIFO_FILE, O_RDONLY);
      read_bytes = read(fd, readbuf, sizeof(readbuf));
      readbuf[read_bytes] = '\0';
      printf("Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      to_end = strcmp(readbuf, end);
      if (to_end == 0) {
         close(fd);
         break;
      }
   }
   return 0;
}

コンパイルおよび実行手順

Received string: "this is string 1" and length is 16
Received string: "fifo test" and length is 9
Received string: "fifo client and server" and length is 22
Received string: "end" and length is 3

では、FIFOクライアントのサンプルコードを見てみましょう。

/*Filename: fifoclient.c*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "MYFIFO"
int main() {
   int fd;
   int end_process;
   int stringlen;
   char readbuf[80];
   char end_str[5];
   printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
   fd = open(FIFO_FILE, O_CREAT|O_WRONLY);
   strcpy(end_str, "end");

   while (1) {
      printf("Enter string: ");
      fgets(readbuf, sizeof(readbuf), stdin);
      stringlen = strlen(readbuf);
      readbuf[stringlen - 1] = '\0';
      end_process = strcmp(readbuf, end_str);

     //printf("end_process is %d\n", end_process);
      if (end_process != 0) {
         write(fd, readbuf, strlen(readbuf));
         printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
      } else {
         write(fd, readbuf, strlen(readbuf));
         printf("Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         close(fd);
         break;
      }
   }
   return 0;
}

到着した出力を見てみましょう。

コンパイルおよび実行手順

FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: this is string 1
Sent string: "this is string 1" and string length is 16
Enter string: fifo test
Sent string: "fifo test" and string length is 9
Enter string: fifo client and server
Sent string: "fifo client and server" and string length is 22
Enter string: end
Sent string: "end" and string length is 3

名前付きパイプを使用した双方向通信

パイプ間の通信は単方向であることを意図しています。 パイプは一般に一方向通信に制限されており、双方向通信には少なくとも2本のパイプが必要です。 パイプは、相互に関連するプロセス専用です。 パイプは無関係なプロセスの通信に使用できません。たとえば、ある端末から1つのプロセスを実行し、別の端末から別のプロセスを実行する場合、パイプでは不可能です。 2つのプロセス間で通信する簡単な方法はありますか? 答えはイエスです。 名前付きパイプは、2つ以上の無関係なプロセス間の通信を目的としており、双方向通信も可能です。

すでに、名前付きパイプ間の一方向の通信、つまりクライアントからサーバーへのメッセージを見てきました。 ここで、双方向通信、つまり、クライアントがサーバーにメッセージを送信し、サーバーがメッセージを受信し、同じ名前付きパイプを使用して別のメッセージをクライアントに送信することを見てみましょう。

以下は例です-

  • ステップ1 *-2つのプロセスを作成します。1つはfifoserver_twoway、もう1つはfifoclient_twowayです。
  • ステップ2 *-サーバープロセスは以下を実行します-
  • /tmpディレクトリに「fifo_twoway」という名前の名前付きパイプを(ライブラリ関数mkfifo()を使用して)作成します(作成されていない場合)。 * 読み取りおよび書き込み目的で名前付きパイプを開きます。 * ここでは、所有者の読み取りと書き込みの権限を持つFIFOを作成しました。 グループの読み取り、その他のアクセス許可はありません。 * クライアントからのメッセージを無限に待ちます。 * クライアントから受信したメッセージが「終了」ではない場合、メッセージを出力して文字列を反転します。 反転した文字列はクライアントに送り返されます。 メッセージが「終了」の場合、FIFOを閉じてプロセスを終了します。
  • ステップ3 *-クライアントプロセスは以下を実行します-
  • 読み取りおよび書き込み目的で名前付きパイプを開きます。
  • ユーザーからの文字列を受け入れます。
  • ユーザーが「終了」または「終了」以外を入力したかどうかを確認します。 いずれにせよ、サーバーにメッセージを送信します。 ただし、文字列が「終了」の場合、これによりFIFOが閉じられ、プロセスも終了します。
  • メッセージが「終了」ではないとして送信された場合、クライアントからのメッセージ(反転した文字列)を待機し、反転した文字列を出力します。
  • ユーザーが文字列「end」を入力するまで無限に繰り返します。

では、FIFOサーバーのサンプルコードを見てみましょう。

/*Filename: fifoserver_twoway.c*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "/tmp/fifo_twoway"
void reverse_string(char *);
int main() {
   int fd;
   char readbuf[80];
   char end[10];
   int to_end;
   int read_bytes;

  /*Create the FIFO if it does not exist*/
   mkfifo(FIFO_FILE, S_IFIFO|0640);
   strcpy(end, "end");
   fd = open(FIFO_FILE, O_RDWR);
   while(1) {
      read_bytes = read(fd, readbuf, sizeof(readbuf));
      readbuf[read_bytes] = '\0';
      printf("FIFOSERVER: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      to_end = strcmp(readbuf, end);

      if (to_end == 0) {
         close(fd);
         break;
      }
      reverse_string(readbuf);
      printf("FIFOSERVER: Sending Reversed String: \"%s\" and length is %d\n", readbuf, (int) strlen(readbuf));
      write(fd, readbuf, strlen(readbuf));
     /*
      sleep - This is to make sure other process reads this, otherwise this
      process would retrieve the message
      */
      sleep(2);
   }
   return 0;
}

void reverse_string(char *str) {
   int last, limit, first;
   char temp;
   last = strlen(str) - 1;
   limit = last/2;
   first = 0;

   while (first < last) {
      temp = str[first];
      str[first] = str[last];
      str[last] = temp;
      first++;
      last--;
   }
   return;
}

コンパイルおよび実行手順

FIFOSERVER: Received string: "LINUX IPCs" and length is 10
FIFOSERVER: Sending Reversed String: "sCPI XUNIL" and length is 10
FIFOSERVER: Received string: "Inter Process Communication" and length is 27
FIFOSERVER: Sending Reversed String: "noitacinummoC ssecorP retnI" and length is 27
FIFOSERVER: Received string: "end" and length is 3

では、FIFOクライアントのサンプルコードを見てみましょう。

/*Filename: fifoclient_twoway.c*/
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

#define FIFO_FILE "/tmp/fifo_twoway"
int main() {
   int fd;
   int end_process;
   int stringlen;
   int read_bytes;
   char readbuf[80];
   char end_str[5];
   printf("FIFO_CLIENT: Send messages, infinitely, to end enter \"end\"\n");
   fd = open(FIFO_FILE, O_CREAT|O_RDWR);
   strcpy(end_str, "end");

   while (1) {
      printf("Enter string: ");
      fgets(readbuf, sizeof(readbuf), stdin);
      stringlen = strlen(readbuf);
      readbuf[stringlen - 1] = '\0';
      end_process = strcmp(readbuf, end_str);

     //printf("end_process is %d\n", end_process);
      if (end_process != 0) {
         write(fd, readbuf, strlen(readbuf));
         printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         read_bytes = read(fd, readbuf, sizeof(readbuf));
         readbuf[read_bytes] = '\0';
         printf("FIFOCLIENT: Received string: \"%s\" and length is %d\n", readbuf, (int)strlen(readbuf));
      } else {
         write(fd, readbuf, strlen(readbuf));
         printf("FIFOCLIENT: Sent string: \"%s\" and string length is %d\n", readbuf, (int)strlen(readbuf));
         close(fd);
         break;
      }
   }
   return 0;
}

コンパイルおよび実行手順

FIFO_CLIENT: Send messages, infinitely, to end enter "end"
Enter string: LINUX IPCs
FIFOCLIENT: Sent string: "LINUX IPCs" and string length is 10
FIFOCLIENT: Received string: "sCPI XUNIL" and length is 10
Enter string: Inter Process Communication
FIFOCLIENT: Sent string: "Inter Process Communication" and string length is 27
FIFOCLIENT: Received string: "noitacinummoC ssecorP retnI" and length is 27
Enter string: end
FIFOCLIENT: Sent string: "end" and string length is 3