Inter-process-communication-message-queues

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

メッセージキュー

共有メモリが既にあるのに、なぜメッセージキューが必要なのですか? それは複数の理由によるものです、単純化のためにこれを複数のポイントに分割してみましょう-

  • 理解されているように、メッセージがプロセスによって受信されると、他のプロセスで使用できなくなります。 一方、共有メモリでは、データは複数のプロセスがアクセスできます。
  • 小さなメッセージ形式で通信したい場合。
  • 複数のプロセスが同時に通信する場合は、共有メモリデータを同期で保護する必要があります。
  • 共有メモリを使用した書き込みおよび読み取りの頻度が高い場合、機能の実装は非常に複雑になります。 この種のケースでの使用に関しては価値がありません。
  • すべてのプロセスが共有メモリにアクセスする必要はないが、それだけを必要とするプロセスが非常に少ない場合は、メッセージキューを使用して実装する方が良いでしょう。
  • 異なるデータパケットと通信する場合、プロセスAがメッセージタイプ1をプロセスBに、メッセージタイプ10をプロセスCに、メッセージタイプ20をプロセスDに送信するとします。 この場合、メッセージキューを使用して実装する方が簡単です。 指定されたメッセージタイプを1、10、20として単純化するには、以下で説明するように、0または+ veまたは-veのいずれかです。
  • もちろん、メッセージキューの順序はFIFO(先入れ先出し)です。 キューに挿入される最初のメッセージは、取得される最初のメッセージです。

共有メモリまたはメッセージキューを使用するかどうかは、アプリケーションの必要性と、それをどの程度効果的に利用できるかによって異なります。

メッセージキューを使用した通信は、次の方法で発生する可能性があります-

  • あるプロセスによる共有メモリへの書き込みと、別のプロセスによる共有メモリからの読み取り。 認識しているように、読み取りは複数のプロセスでも実行できます。

メッセージキュー

  • 異なるデータパケットを使用した1つのプロセスによる共有メモリへの書き込みと、複数のプロセスによる、つまりメッセージタイプごとの共有メモリからの読み取り。

複数のメッセージキュー

メッセージキューに関する特定の情報を見たので、今度はメッセージキューをサポートするシステムコール(System V)を確認します。

メッセージキューを使用して通信を実行するには、次の手順があります-

  • ステップ1 *-メッセージキューを作成するか、既存のメッセージキューに接続する(msgget())
  • ステップ2 *-メッセージキューに書き込む(msgsnd())
  • ステップ3 *-メッセージキューからの読み取り(msgrcv())
  • ステップ4 *-メッセージキューで制御操作を実行する(msgctl())

次に、上記の呼び出しの構文と特定の情報を確認しましょう。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg)

このシステムコールは、System Vメッセージキューを作成または割り当てます。 次の引数を渡す必要があります-

  • 最初の引数keyは、メッセージキューを認識します。 キーは、任意の値でも、ライブラリ関数ftok()から派生した値でもかまいません。
  • 2番目の引数shmflgは、IPC_CREAT(存在しない場合はメッセージキューを作成する)またはIPC_EXCL(IPC_CREATとともに使用してメッセージキューを作成し、メッセージキューが既に存在する場合は呼び出しが失敗する)などの必要なメッセージキューフラグを指定します。 許可も渡す必要があります。

-権限の詳細については、前のセクションを参照してください。

この呼び出しは、成功した場合は有効なメッセージキュー識別子(メッセージキューの以降の呼び出しに使用)を返し、失敗した場合は-1を返します。 失敗の原因を知るには、errno変数またはperror()関数で確認してください。

この呼び出しに関するさまざまなエラーは、EACCESS(許可が拒否されました)、EEXIST(既に存在するキューは作成できません)、ENOENT(キューが存在しません)、ENOMEM(キューを作成するのに十分なメモリがありません)などです。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msgid, const void *msgp, size_t msgsz, int msgflg)

このシステムコールは、メッセージをメッセージキューに送信/追加します(システムV)。 次の引数を渡す必要があります-

  • 最初の引数msgidは、メッセージキュー、つまりメッセージキュー識別子を認識します。 msgget()の成功時に識別子の値を受け取ります
  • 2番目の引数、msgpは、次の形式の構造で定義された、呼び出し元に送信されるメッセージへのポインタです-
struct msgbuf {
   long mtype;
   char mtext[1];
};

変数mtypeは、さまざまなメッセージタイプとの通信に使用されます。詳細については、msgrcv()呼び出しで説明しています。 変数mtextは、msgsz(正の値)でサイズが指定された配列またはその他の構造です。 mtextフィールドが言及されていない場合、それは許可されているゼロサイズのメッセージと見なされます。

  • 3番目の引数msgszは、メッセージのサイズです(メッセージはヌル文字で終了する必要があります) *4番目の引数msgflgは、IPC_NOWAITなどの特定のフラグを示します(キューにメッセージが見つからない場合はすぐに戻ります。MSG_NOERROR(msgszバイトを超える場合はメッセージテキストを切り捨てます)

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

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgrcv(int msgid, const void* msgp, size_t msgsz, long msgtype, int msgflg)

このシステムコールは、メッセージキュー(システムV)からメッセージを取得します。 次の引数を渡す必要があります-

  • 最初の引数msgidは、メッセージキュー、つまりメッセージキュー識別子を認識します。 msgget()の成功時に識別子の値を受け取ります
  • 2番目の引数msgpは、呼び出し元から受信したメッセージのポインターです。 それは、次の形式の構造で定義されています-
struct msgbuf {
   long mtype;
   char mtext[1];
};

変数mtypeは、異なるメッセージタイプとの通信に使用されます。 変数mtextは、msgsz(正の値)でサイズが指定された配列またはその他の構造です。 mtextフィールドが言及されていない場合、それは許可されているゼロサイズのメッセージと見なされます。

  • 3番目の引数msgszは、受信したメッセージのサイズです(メッセージはヌル文字で終了する必要があります)
  • fouth引数、msgtypeは、メッセージのタイプを示しています-
  • * msgtypeが0の場合-キュー内の最初の受信メッセージを読み取ります
  • msgtypeが+ ve の場合-タイプmsgtypeのキュー内の最初のメッセージを読み取ります(msgtypeが10の場合、他のタイプが最初にキューにある場合でもタイプ10の最初のメッセージのみを読み取ります)
  • msgtypeが-ve の場合-メッセージタイプの絶対値以下の最低タイプの最初のメッセージを読み取ります(たとえば、msgtypeが-5の場合、5より小さいタイプの最初のメッセージを読み取ります。つまり、メッセージタイプは1から5) *5番目の引数msgflgは、IPC_NOWAITなどの特定のフラグを示します(キューにメッセージが見つからない場合はすぐに戻ります。MSG_NOERROR(msgszバイトを超える場合はメッセージテキストを切り捨てます)

この呼び出しは、成功するとmtext配列で実際に受信したバイト数を返し、失敗した場合は-1を返します。 失敗の原因を知るには、errno変数またはperror()関数で確認してください。

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgctl(int msgid, int cmd, struct msqid_ds* buf)

このシステムコールは、メッセージキュー(システムV)の制御操作を実行します。 次の引数を渡す必要があります-

  • 最初の引数msgidは、メッセージキュー、つまりメッセージキュー識別子を認識します。 msgget()の成功時に識別子の値を受け取ります

  • 2番目の引数cmdは、メッセージキューで必要な制御操作を実行するコマンドです。 cmdの有効な値は-

    *IPC_STAT* -struct msqid_dsの各メンバーの現在の値の情報を、bufが指す渡された構造にコピーします。 このコマンドには、メッセージキューの読み取り権限が必要です。
    *IPC_SET* -構造bufが指すユーザーID、所有者のグループID、権限などを設定します。
    *IPC_RMID* -メッセージキューをすぐに削除します。
    *IPC_INFO* -タイプstruct msginfoであるbufが指す構造体のメッセージキュー制限とパラメーターに関する情報を返します
    *MSG_INFO* -メッセージキューによって消費されたシステムリソースに関する情報を含むmsginfo構造体を返します。
  • 3番目の引数bufは、struct msqid_dsという名前のメッセージキュー構造体へのポインターです。 この構造の値は、cmdに従ってsetまたはgetに使用されます。

この呼び出しは、渡されたコマンドに応じて値を返します。 IPC_INFOおよびMSG_INFOまたはMSG_STATが成功すると、メッセージキューのインデックスまたは識別子が返されます。他の操作の場合は0、失敗の場合は-1が返されます。 失敗の原因を知るには、errno変数またはperror()関数で確認してください。

メッセージキューに関する基本的な情報とシステムコールを確認したら、今度はプログラムで確認します。

プログラムを見る前に説明を見てみましょう-

  • ステップ1 *-2つのプロセスを作成します。1つはメッセージキュー(msgq_send.c)に送信するためのもので、もう1つはメッセージキュー(msgq_recv.c)から取得するためのものです
  • ステップ2 *-ftok()関数を使用してキーを作成します。 このため、一意のキーを取得するために、最初にファイルmsgq.txtが作成されます。
  • ステップ3 *-送信プロセスは以下を実行します。
  • ユーザーから入力された文字列を読み取ります
  • 存在する場合、新しい行を削除します
  • メッセージキューに送信する
  • 入力が終了するまでプロセスを繰り返します(CTRL + D)
  • 入力の終了を受信したら、メッセージ「end」を送信してプロセスの終了を通知します
  • ステップ4 *-受信プロセスで、以下を実行します。
  • キューからメッセージを読み取ります
  • 出力を表示します
  • 受信したメッセージが「終了」の場合、プロセスを終了して終了します

単純化するために、このサンプルではメッセージタイプを使用していません。 また、1つのプロセスがキューに書き込み、別のプロセスがキューから読み取りを行っています。 これは必要に応じて拡張できます。つまり、理想的には、1つのプロセスがキューに書き込み、複数のプロセスがキューから読み取ります。

次に、プロセス(メッセージをキューに送信)を確認します–ファイル:msgq_send.c

/*Filename: msgq_send.c*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int len;
   key_t key;
   system("touch msgq.txt");

   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }

   if ((msqid = msgget(key, PERMS | IPC_CREAT)) == -1) {
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to send messages.\n");
   printf("Enter lines of text, ^D to quit:\n");
   buf.mtype = 1;/*we don't really care in this case*/

   while(fgets(buf.mtext, sizeof buf.mtext, stdin) != NULL) {
      len = strlen(buf.mtext);
     /*remove newline at end, if it exists*/
      if (buf.mtext[len-1] == '\n') buf.mtext[len-1] = '\0';
      if (msgsnd(msqid, &buf, len+1, 0) == -1)/*+1 for '\0'*/
      perror("msgsnd");
   }
   strcpy(buf.mtext, "end");
   len = strlen(buf.mtext);
   if (msgsnd(msqid, &buf, len+1, 0) == -1)/*+1 for '\0'*/
   perror("msgsnd");

   if (msgctl(msqid, IPC_RMID, NULL) == -1) {
      perror("msgctl");
      exit(1);
   }
   printf("message queue: done sending messages.\n");
   return 0;
}

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

message queue: ready to send messages.
Enter lines of text, ^D to quit:
this is line 1
this is line 2
message queue: done sending messages.

以下は、メッセージ受信プロセスからのコードです(キューからメッセージを取得)–ファイル:msgq_recv.c

/*Filename: msgq_recv.c*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

#define PERMS 0644
struct my_msgbuf {
   long mtype;
   char mtext[200];
};

int main(void) {
   struct my_msgbuf buf;
   int msqid;
   int toend;
   key_t key;

   if ((key = ftok("msgq.txt", 'B')) == -1) {
      perror("ftok");
      exit(1);
   }

   if ((msqid = msgget(key, PERMS)) == -1) {/*connect to the queue*/
      perror("msgget");
      exit(1);
   }
   printf("message queue: ready to receive messages.\n");

   for(;;) {/* normally receiving never ends but just to make conclusion
            /*this program ends wuth string of end*/
      if (msgrcv(msqid, &buf, sizeof(buf.mtext), 0, 0) == -1) {
         perror("msgrcv");
         exit(1);
      }
      printf("recvd: \"%s\"\n", buf.mtext);
      toend = strcmp(buf.mtext,"end");
      if (toend == 0)
      break;
   }
   printf("message queue: done receiving messages.\n");
   system("rm msgq.txt");
   return 0;
}

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

message queue: ready to receive messages.
recvd: "this is line 1"
recvd: "this is line 2"
recvd: "end"
message queue: done receiving messages.