POSIXメッセージキュー for Windows
Linux や BSD 系 Unix には POSIX メッセージキューという便利な API があります。この便利な POSIX メッセージキューを Windows (C/C++) でも使えるように実装しました。
POSIX メッセーキューはプロセス間通信 (IPC:Inter-Process Communication) 技術の 1 つで、 複数のプロセス間でメッセージのやりとりを行うことができます。
メッセージキューとソケット通信の違い
POSIX メッセージキューはソケット通信とは異なり送信側と受信側が同時に存在していなくても良い、 という特徴があります。ソケット通信の場合には一方のプロセスがもう一方のプロセスにソケットを接続して通信をおこなうため 2 つのプロセスは同時に存在している必要があります。ソケット通信では受信側プロセスが存在しない状態では送信側プロセスはデータを送信することができないわけです。
POSIX メッセージキューの場合はプロセスの生存期間とは無関係に永続性を持つメッセージキューが存在します。送信側プロセスは受信側プロセスの有無に関わらずメッセージキューにデータを書き込むことができます。同様に受信側プロセスは送信側プロセスの有無に関わらずメッセージキューからデータを読み出すことができます。
また、 POSIX メッセージキューにはメッセージの優先度というものがあります。ソケット通信では送信された順序でしかデータを受信することはできませんが、 メッセージキューでは優先度の高いメッセージが優先度の低いメッセージを追い越して先にメッセージキューから読み出されます。優先度を上手く使うと緊急性の高い処理や例外的な処理を優先的に扱うようにアプリケーションを設計できます。
ダウンロード
本ソフトウェアは MIT ライセンスに基づいて無償で提供されます。
ソースコードは mqueue.c と mqueue.h のみです。Visual Studio (VC++) だけでなく、 MinGW GCC でもコンパイルできるようになっています。
ライブラリとしてビルドすることもできますし、 ライブラリとしてビルドせずに mqueue.c と mqueue.h をプロジェクトに追加して、 そのままアプリケーションと一緒にビルドすることもできます。
使い方
POSIX メッセージキューの使い方は Linux 実装と同じです。Linux の POSIX メッセージキューの概要については以下を参照してください。Windows 版でも API の使用方法は同じなので、 Windows で POSIX メッセージキューを使う時にも役に立つ情報です。
POSIX メッセージキューを構成する関数のうち、 mq_open
、 mq_close
、 mq_send
、 mq_receive
の 4 つの関数の使い方を覚えればメッセージキューを使えるようになります。
メッセージキューをオープンする
メッセージキューを読み書きするプロセスは、 はじめに mq_open
関数を使ってメッセージキューをオープンする必要があります。
メッセージキューをオープンするint oflag = O_CREAT | O_RDWR;
int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
mqd_t mqdes = mq_open("/my-queue1", oflag, mode, NULL);
if(mqdes == (mqd_t)-1)
{
//open error
}
- メッセージキューの名前は必ず
/
で始まる必要があります。 - 引数に
O_CREAT
フラグを指定することで、 メッセージキューが存在しない場合に自動的にメッセージキューを作成するように指定することができます。 - メッセージキューに書き込みをする場合には
O_WRONLY
、 読み取りをする場合にはO_RDONLY
、 読み書き両方をする場合にはO_ORWR
を指定します。 - 第 3 引数 mode にはメッセージキューのオープンモード (アクセス権) を指定します。制限する必要がない場合は上記例のように
S_IRUSR
,S_IWUSR
,S_IRGRP
,S_IWGRP
,S_IROTH
,S_IWOTH
を指定しましょう。 - 第 2 引数 oflag に
O_NONBLOCK
が含まれているとノンブロッキングモードになります。
mq_open
関数の戻り値はメッセージキュー記述子 (ディスクリプタ) です。ただし、 メッセージキューのオープンに失敗した場合は有効なメッセージキュー記述子ではなく -1 が返されます。
メッセージを送信する
メッセージの送信には mq_send
関数を使用します。
"HELLO" というメッセージを送信するint ret = mq_send(mqdes, "HELLO", 6, 0);
if(ret == -1)
{
//send error
}
これは “HELLO” というメッセージを送信する例です。
- 第 1 引数に指定している
mqdes
変数はmq_open
関数で取得したメッセージキュー記述子 (ディスクリプタ) です。 - 第 2 引数には送信するデータのポインターを指定します。
- 第 3 引数には送信するデータの長さを指定します。
- 第 4 引数にはメッセージの優先度を指定します。0 が優先度がもっとも低く、 数字が大きくなるほど優先度が高くなります。
POSIX メッセージキューでは文字列に限らず任意のバイナリデータをメッセージとして送信することができます。文字列を送信する場合にはヌル終端文字も送信されるように文字列の長さ +1 をデータの長さとして指定する必要があります。(この例では “HELLO” の長さ 5 にヌル終端文字の分を加えて 6 となります。)
POSIX ではメッセージの優先度として上限を少なくとも 31 と定めていますが、 多くの実装でより大きな値を扱えるようになっています。この Windows 実装では優先度の型を unsigned int
としているので、 0 ~ 4,294,967,295 の範囲で優先度を指定することができます。
メッセージを受信する
メッセージを受信するには mq_receive
関数を使用します。
メッセージを受信するchar msg[BUFSIZ];
unsigned int msg_prio;
ssize_t len;
len = mq_receive(mqdes, msg, BUFSIZ, &msg_prio);
if(len == -1)
{
//receive error
}
これはメッセージキューに溜まっているメッセージを受信する例です。
- 第 1 引数には
mq_open
関数で取得したメッセージキュー記述子 (ディスクリプタ) を指定します。 - 第 2 引数には受信データを書き込むバッファのポインターを指定します。
- 第 3 引数には第 2 引数で指定したバッファのサイズを指定します。
- 第 4 引数には
unsigned int
変数のアドレスを指定します。この変数にメッセージの優先度が設定されます。
簡略化するためにバッファのサイズを BUFSIZ
としていますが、 実際にはメッセージキューのメッセージ最大サイズを格納できるバッファを用意する必要があります。mq_getattr
関数を使用してメッセージキューの属性情報を取得することで、 正確なメッセージ最大サイズを知ることができます。
mq_receive
の戻り値は受信したメッセージデータの長さです。エラーの場合には -1
が返されます。
メッセージキューが空の状態で mq_receive
を呼び出した場合の動作は、 mq_open
関数でメッセージキューをオープンする時に O_NONBLOCK
を指定していたかどうかで異なります。
mq_open
関数で O_NONBLOCK
を指定していなかった場合はブロッキングモードです。つまり、 メッセージキューが空の状態で mq_receive
を呼び出すと、 関数からすぐに復帰せずに待機状態になります。他のプロセスがメッセージキューにデータを書き込むと、 mq_receive
関数の呼び出しから復帰します。
mq_open
関数で O_NONBLOCK
を指定していた場合はノンブロッキングモードです。メッセージキューが空の状態で mq_receive
を呼び出すと、 関数からすぐに復帰して -1
を返します。このとき、 errno
には EAGAIN
が設定されるので、 errno
の値を確認することで空状態による復帰か、 その他のエラーによる復帰かを区別することができます。
メッセージキュー記述子をクローズする
メッセージキュー記述子をクローズするには mq_close
を使用します。
メッセージキュー記述子をクローズするmq_close(mqdes);
引数として mq_open
関数で取得したメッセージキュー記述子を指定します。
この関数はメッセージキュー自体を閉じるのではなく、 メッセージキュー記述子を閉じるものであることに注意してください。mq_open
関数でメッセージキュー記述子を取得すると、 内部では malloc
関数によってヒープメモリが確保されたり、 メッセージキューファイルのハンドル、 同期制御用のミューテックスハンドルなどのリソースが作成されます。mq_close
関数は、 これらのメッセージキュー記述子に紐づけされたリソースを解放します。
mq_close
を呼び出してもメッセージキュー自体は影響を受けません。つまり、 書き込みプロセスが mq_close
を呼び出しても、 別の読み込みプロセスが mq_open
で取得したメッセージキュー記述子は引き続き有効でメッセージキューからの読み取り操作をおこなうことができます。
他にもいろいろな関数があります
POSIX メッセージキュー API には他にも多くの関数があります。
mq_getattr
- メッセージキューの属性を取得します。
mq_setattr
- メッセージキューの属性を設定します。
mq_timedsend
- メッセージキューにメッセージを送信します。ブロッキングモードで待機状態になっても指定時間で復帰します。
mq_timedreceive
- メッセージキューからメッセージを受信します。ブロッキングモードで待機状態になっても指定時間で復帰します。
mq_unlink
- メッセージキュー自体を削除します。
これらの関数は Windows 実装においても Linux と同様に動作しますので、 関数の仕様や使い方については Linux のドキュメント (POSIX メッセージキューの概要) を参考にしてみてください。
変更履歴
v0.1 (2018-02-18)
- 初版リリースです。