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

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

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

メッセージキューとは?

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

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

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


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

  1. #include <sys/types.h>
  2. #include <stdio.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <string.h>
  6. #include <unistd.h>
  7. #define MSGBUFSIZ 256
  8. int main(int argc, char** argv) {
  9. int msqid;
  10. key_t msgkey;
  11. struct msgbuf{
  12. long mtype;
  13. char mdata[MSGBUFSIZ];
  14. };
  15. struct msgbuf msgdata,*p;
  16. p = &msgdata;
  17. // Make Message queue key.
  18. msgkey = ftok("msgq.key",'X');
  19. // Get Message queue ID.
  20. msqid = msgget(msgkey,IPC_CREAT|0666);
  21. // Message receive loop wile get "Quit" massage.
  22. while(1) {
  23. // Receive a Message.
  24. if(msgrcv(msqid,p,sizeof(p->mdata),0,IPC_NOWAIT)<=0) {
  25. printf("No message\n");
  26. sleep(3);
  27. continue;
  28. }
  29. printf("message received from %ld\n%s\n",p->mtype,p->mdata);
  30. if(strncmp((char*)p->mdata, "Quit", (size_t)3)==0)
  31. break;
  32. }
  33. // Remove Message Queue
  34. msgctl(msqid, IPC_RMID, 0);
  35. }


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

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

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


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

  1. #include <stdio.h>
  2. #include <sys/types.h>
  3. #include <sys/ipc.h>
  4. #include <sys/msg.h>
  5. #include <unistd.h>
  6. #define MSGBUFSIZ 256
  7. int main(int argc, char** argv) {
  8. int msqid;
  9. key_t msgkey;
  10. struct msgbuf{
  11. long mtype;
  12. char mdata[MSGBUFSIZ];
  13. };
  14. struct msgbuf msgdata,*p;
  15. // Make Message queue key.
  16. msgkey = ftok(argv[1], 'X');
  17. // Get Message queue ID. (Only get existing message queue.)
  18. msqid = msgget(msgkey, 0666);
  19. printf("QID = %d\n", msqid);
  20. if(msqid<0 -1="" fflush="" fgets="" nter="" p-="" p="&msgdata;" printf="" return="" stdin="">mdata,MSGBUFSIZ,stdin);
  21. p->mtype = getpid();
  22. // Message send.
  23. msgsnd(msqid,p,sizeof(p->mdata),0);
  24. }

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

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

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

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


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


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

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

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

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



Lightning Brains

コメント

このブログの人気の投稿

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

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