Unix系OSでのIPCの手段として提供されている機能の中にメッセージキューという機能があります。
この機能はプロセス間のデータの受け渡しに便利ですが、そもそもプロセス間通信とかたまにしか使わないので備忘のためまとめておきたいと思います。
目次
メッセージキューとは
プロセス間通信の手法の1つです。
メッセージキューに関する注意点として、SystemVとPOSIXで2修理のメッセージキューのAPIが存在します。
ちなみにSystem-V版のメッセージキュー(msgget, msgrcv, msgsnd)は、詳解Unixプログラミング(第3版)では
「遅いしこれからは使わない方がいいぞ」
という扱いを受けています。
今回はPOSIXのメッセージキューについて紹介します。
RTOSのitronだと任意のデータの受け渡しはメールボックス(snd_mbx,rcv_mbx)がよく使われると思いますが、POSIXのメッセージキューは似たような感じで使えます。
APIの簡単な説明
初期化
mq_open関数を使います。
注意点として O_CREAT を指定して作成する際はキューのパラメータ(キューサイズ、1メッセージのサイズ)を指定可能です。
指定しなかった場合は/procに記載されているデフォルト値で動作します。
送受信
mq_send, mq_receive 関数を使います。
メリットとデメリット
メリット
キューであること
文字通り「メッセージ」の「キュー」であることが保証されていることでしょう。
プロセス間通信の方法として提供されている、名前付きパイプやとsocketでも任意のデータの受け渡しが可能ですのでまぁこの点はメリットとしては少し弱い気もします。
優先度が指定できる
優先度付きキューであるので、他のプロセス間通信と違って優先度が必要な場合作りこみが不要*1
送信と受信の呼び出し順を意識しなくてよい
unlink されない限りはキューにメッセージが残ります。
カーネルでキューを管理しているので、送信側プロセスで送信してから、受信側プロセスを起動してもメッセージを受信できます。
ただしキュー自体が存在することが前提なので誰がキューを作成するのか(mq_open時のO_CREATの指定)は考えておく必要がある
送信側が待たされない
例えば名前付きパイプ(FIFO)は受信側のプロセスが送信側のwriteするまで待たされます。
本当に同期的に動作するプログラムなら名前付きパイプで問題ないかもしれませんがそのような動作を期待するケースというはあまりないように思います。
メッセージキューであれば送信側はブロックされないので非同期的に何かを実行したい場合は便利です。
サンプルコード
送信するプログラムと受信するプログラムの例を挙げておきます。
サンプルでは、受信側でキューの作成を行っているので受信側プログラム(mq_recv_sample.c)を先に起動する必要があります。
*5
Linuxでの実装
Linuxでは、ipc/mqueue.c, ipc/mqueue.h で実装されています。
Linuxカーネルの機能として実装されているので、glibcではmq_xxx関数は未サポートのシステムコールエラーを返すような実装になっていますね。
ちらっと実装を見てみましたが、受信側プロセスが待ち状態にある場合はエンキューせずに直接メッセージを渡すという工夫(pipelined_send)がされていました。
同時に1メッセージしか受け取らない場合はパフォーマンスがよいのかもしれません。
まとめ
ということで、POSIXメッセージキューはプロセス間で相手方にシーケンシャルな要求を行うのに便利な機能です。
POSIXメッセージキューのようなプロセス間通信はカーネルが間に介在するため正しい実装方法が分かりにくいことが多いですが、Linuxプログラミングインターフェースには詳しい説明が記載されています。
追記したumaskについても触れられているのでLinuxで開発する人は手元に一冊置いておくと安心感があります。
追記
POSIXメッセージキューですがキュー作成するときにumaskによって権限が制約され、他のユーザー権限で動作するプロセスからはアクセスできない場合があります。*6その場合はmq_openする前に umask(0)することで回避できます。キュー作成後はマスク値はもとに戻しましょう。
追記2
キューのcloseをし忘れた状態で同じキューにmq_openし続けているとerrno 24が発生します。
このエラーはmq_open/mq_closeするプロセスが別であっても発生しきづきにくいです。