Inter-process-communication-message-queues
メッセージキュー
共有メモリが既にあるのに、なぜメッセージキューが必要なのですか? それは複数の理由によるものです、単純化のためにこれを複数のポイントに分割してみましょう-
- 理解されているように、メッセージがプロセスによって受信されると、他のプロセスで使用できなくなります。 一方、共有メモリでは、データは複数のプロセスがアクセスできます。
- 小さなメッセージ形式で通信したい場合。
- 複数のプロセスが同時に通信する場合は、共有メモリデータを同期で保護する必要があります。
- 共有メモリを使用した書き込みおよび読み取りの頻度が高い場合、機能の実装は非常に複雑になります。 この種のケースでの使用に関しては価値がありません。
- すべてのプロセスが共有メモリにアクセスする必要はないが、それだけを必要とするプロセスが非常に少ない場合は、メッセージキューを使用して実装する方が良いでしょう。
- 異なるデータパケットと通信する場合、プロセス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.