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)

 

戻る