IPC: メッセージ・キュー
メッセージキューによる情報交換。
SYNOPSIS
#include <sys/msg.h> #include <unistd.h> int msgget(key_t key, int msgflg); int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg); int msgctl(int msqid, int cmd, struct msqid_ds *buf);
DESCRIPTION
メッセージ・キューを用いた情報交換の方法を簡単にまとめて見ます。新しくメッセージ・キューを作成し、情報を交換可能することも可能だし、既存のものに接続することも可能です。作成したキューは、破棄するまで存在し続けます。ipcs
コマンドでメッセージ・キューを確認できます。
● メッセージ・キューの作成/接続は、msgget()
システムコールを使用します。
int msgget(key_t key, int msgflg);
msgget()
システムコールは、作成や接続に成功するとユニークな ID を返し、失敗すると -1
を返します。
key
とは、作成または接続したいメッセージ・キューでユニークなものを識別子として指定します。このメッセージ・キューに接続したい場合、他のすべてのプロセスは同じ key
を使用しなければなりません。msgflg
は、キューへのアクセスパーミッションを指定できます。また、メッセージ・キューを作成する場合、IPC_CREAT
をセットしなければなりません。
key
の決定は、ユニークなものであれば適当な数値でかまいません。しかし、なかなかユニークな数値を設定するのが困難な場合 ftok()
関数を使うとよいでしょう。この関数は、ファイルから情報を得て数値を決定します。また、同じファイルを使っても出来るだけユニークな数値になるように二つ目の引数で一文字指定するようになっています。
● メッセージの送信は、msgsnd()
システムコールを使用します。
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msqid
は、msgget()
システムコールによって作成/接続されたメッセージ・キューの ID です。msgp
は、メッセージを指定します。デフォルトでメッセージの構造が用意されており、それを基にメッセージを作成できます。
struct mymsg { long mtype; /* message type */ char mtext[1]; /* message text */ }
デフォルトの構造で不便な場合、自分で作成します。(mtype
は必ず必要です。)
struct mymsg { long mtype; /* message type */ char mtext[BUFSIZ]; /* message text */ int value1; int value2; }
msgp
は、メッセージのサイズ。msgflg
は、フラグを設定することが出来ますが、必要がなければ 0
を指定します。
● メッセージの受信は、msgrcv()
システムコールを使用します。
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long int msgtyp, int msgflg);
msgtyp
以外は、msgsnd()
システムコールと同じです。msgtyp
は、メッセージのタイプを指定します。
0 | mtype に関係なくメッセージすべてを受信する [ALL] |
ポジティブ ( >0 ) | 指定した値と一致する mtype のメッセージを受信する [msgtyp == mtype] |
ネガティブ ( <0 ) | 指定した値以下の mtype のメッセージを受信する [abs(msgtyp) >= mtype] |
● メッセージ・キューの破棄は、 msgctl()
システムコールを使用します。
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
msqid
は、msgget()
システムコールによって作成/接続されたメッセージ・キューの ID です。cmd
は、IPC_RMID
を指定します。IPC_RMID
の場合、buf
は NULL を指定します。
メッセージ・キューの破棄は、ipcs
コマンドで詳細を調べ ipcrm
コマンドを利用しても可能です。
SAMPLE
メッセージ・キューに送信するプログラム(queue_w.c
)
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct my_msgbuf { long mtype; char mtext[BUFSIZ]; }; int main() { struct my_msgbuf buf; int msqid; key_t key; if ((key = ftok("msgq.dat", 'b')) == -1) { perror("ftok"); exit(1); } if ((msqid = msgget(key, 0644 | IPC_CREAT)) == -1) { perror("msgget"); exit(1); } printf("Enter lines of text, ^D to quit:\n"); buf.mtype = 1; while(1) { gets(buf.mtext); if (feof(stdin)) { break; } if (msgsnd(msqid, (struct msgbuf *)&buf, sizeof(buf), 0) == -1) { perror("msgsnd"); break; } } if (msgctl(msqid, IPC_RMID, NULL) == -1) { perror("msgctl"); exit(1); } return 0; }
メッセージ・キューから受信するプログラム
(queue_r.c
)
#include <stdio.h> #include <stdlib.h> #include <errno.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> struct my_msgbuf { long mtype; char mtext[BUFSIZ]; }; int main() { struct my_msgbuf buf; int msqid; key_t key; if ((key = ftok("msgq.dat", 'b')) == -1) { perror("ftok"); exit(1); } if ((msqid = msgget(key, 0644)) == -1) { perror("msgget"); exit(1); } printf("queue: ready to receive messages.\n"); for(;;) { if (msgrcv(msqid, (struct msgbuf *)&buf, sizeof(buf), 0, 0) == -1) { perror("msgrcv"); exit(1); } printf("queue: \"%s\"\n", buf.mtext); } return 0; }
これをコンパイルし実行すると以下のようになります。
<< SHELL-1 >> # gcc queue_w.c -o queue_w # touch msgq.dat # ./queue_w Enter lines of text, ^D to quit: hello, world
SHELL-1 で送信プログラムが起動できたら、別のシェルで受信プログラムを起動します。
<< SHELL-2 >> # gcc queue_r.c -o queue_r # ./queue_r queue: ready to receive messages. queue: "hello, world"
SHELL-1 でメッセージをタイプすると、SHELL-2 にそのメッセージが表示されます。
SEE ALSO
msgget(2), msgsnd(2), msgrcv(2), msgctl(2)