Linuxシステムコール、メッセージキューの使い方

UNIX / Linux システムコール・プログラミング

プロセス間通信(IPC)、メッセージキュー

メッセージキューとは?

いわゆるメッセージボックスのような動作を行う IPC です。
プロセス間で一意に識別できるメッセージキューに対してメッセージを書き込み、受け取りができます。

また、メッセージキューは SYSTEM V IPC です。

メッセージキューを使うためには、まずメッセージキューを作成してユニークなキューIDを作成する必要があります。msgget()というシステムコールでキューを作成します。
メッセージを送信するためには msgsnd()、メッセージを受信するためには msgrcv()、最後にメッセージキューを削除するために msgctl() を使います。


メッセージの受信側プロセスのサンプルコードを下記に示します。

#include <sys/types.h>
#include <stdio.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
#include <unistd.h>

#define MSGBUFSIZ 256

int main(int argc, char** argv) {

  int msqid;
  key_t msgkey;
  struct msgbuf{
    long mtype;
    char mdata[MSGBUFSIZ];
  };
  struct msgbuf msgdata,*p;

  p = &msgdata;
  // Make Message queue key.
  msgkey = ftok("msgq.key",'X');
  // Get Message queue ID.
  msqid = msgget(msgkey,IPC_CREAT|0666);

  // Message receive loop wile get "Quit" massage.
  while(1) {
    // Receive a Message.
    if(msgrcv(msqid,p,sizeof(p->mdata),0,IPC_NOWAIT)<=0) {
      printf("No message\n");
      sleep(3);
      continue;
    }
    printf("message received from %ld\n%s\n",p->mtype,p->mdata);
    if(strncmp((char*)p->mdata, "Quit", (size_t)3)==0)
      break;
  }

  // Remove Message Queue
  msgctl(msqid, IPC_RMID, 0);
}




この例では、メッセージキューの key は、ftok() を使って生成しています。
ftok()は、ファイルパスからキー値を生成します。
 → ftok()を参照
ftok()に与えるパス名のファイルは、touchコマンドなどで作成してあげるとよいでしょう。

そキー値を使って msgget() でメッセージキューを取得します。引数で IPC_CREAT を指定しているので、存在しない場合は生成され、すべてのプロセスに読み書きアクセスを許可としています。

無限ループの中で msgrcv() を使ってメッセージを受信し、"Quit"の文字列を受信した場合は、ループを抜け、msgctl() を使ってメッセージキューを削除しています。
また、msgrcv()はIPC_NOWAITを指定しているのでメッセージが無い場合はすぐに制御がもどり、sleep()で3秒待ってからmsgrcv()を行っています。


次に、メッセージ送信側のサンプルコードです。

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


#define MSGBUFSIZ 256

int main(int argc, char** argv) {
  int msqid;
  key_t msgkey;
  struct msgbuf{
    long mtype;
    char mdata[MSGBUFSIZ];
  };
  struct msgbuf msgdata,*p;

  // Make Message queue key.
  msgkey = ftok(argv[1], 'X');
  // Get Message queue ID. (Only get existing message queue.)
  msqid = msgget(msgkey, 0666);
  printf("QID = %d\n", msqid);
  if(msqid<0 -1="" fflush="" fgets="" nter="" p-="" p="&msgdata;" printf="" return="" stdin="">mdata,MSGBUFSIZ,stdin);
  p->mtype = getpid();
  // Message send.
  msgsnd(msqid,p,sizeof(p->mdata),0);

}


受信側と同様のパス名を ftok() に与えることで受信側と同様のキー値が得られます。
キー値は ftok() ではなく、システムで固定としても問題ありません。

こちらの msgget() では、IPC_CREAT を指定していないので、受信側が先に立ち上がっていないとエラーとなります。

このサンプルでは、メッセージタイプには PID を設定し、メッセージ本文は fgets() で入力された文字列を設定しています。
このとき、ユーザーが"Quit"と入力すれば、受信側が停止します

最後に msgsnd() でメッセージをメッセージキューに送信します。


注意!
このサンプルコードは、システムコールプログラミングの理解のためエラー処理を簡略化しています。実際の業務等で実装する場合は、エラー処理を正しく実装しましょう。


メッセージキューの状況をコマンドで確認する。

ipcsコマンドで、メッセージキューの状態を確認できます。
オプションで"-q"を指定することで、メッセージキューだけ表示することができます。

このサンプルコードは正しく終了した場合、
 msgctl(msqid, IPC_RMID, 0);
で、メッセージキューを削除していますが、Ctrl+cで抜けた場合はメッセージキューが残ってしまいます。
この場合、ipcrmコマンドで削除することができます。
ipcsで確認した、Keyで削除する場合は"-Q Key"、IDで削除する場合は"-q ID"で削除ができます。

プログラム的に対処するには、SIGINTのシグナルハンドラを実装し、その中でメッセージキューの削除を行うようにする必要があります。



Lightning Brains

コメント

このブログの人気の投稿

Linuxシステムコール、共有メモリの使い方

Linuxシステムコール、セマフォの使い方