そんな、ひと夏の衝動にかられて、JNIで共有メモリをアクセスしてみました。
https://github.com/tkymism/sharedmem
今回も流れるインタフェースを駆使して、こんな感じのインタフェースにしてみた訳です。
使い方
(1)VisualStudioでdll生成
- 構成プロパティ➡C/C++-➡全般➡追加のインクルードディレクトリ
- [jdk_dir]\include;
- [jdk_dir]\include\win32;
- [git_dir]\sharedmem\sharedmem-win\include
- 構成プロパティ➡C/C++➡コード生成➡ランタイムライブラリ
- マルチスレッド (/MT)
- 構成プロパティ➡リンカー➡全般➡追加のライブラリディレクトリ
- [jdk_dir]\lib;
- 構成プロパティ➡リンカー➡入力➡追加の依存ファイル
- jvm.lib;
(2)maven installでjar生成
解説
"hogehoge"という名前で共有メモリに書き込んでいます。0~99byteはヘッダーとして使用して、100byte目以降を実態のファイルとしてallocateする図です。
locateでは0~99byte目にアクセスしてByteBuffer(Direct)で共有メモリに直接attachします。 100byte目以降(locate)にもdataを突っ込んで、そしてreleaseする訳です。
やはり、共有メモリに対してアクセスするためには排他制御が必要です。今回は読み込みと書き込み両方でMutexを使って排他制御を行っています。
このプログラムではMutexの名前には"<共有メモリ名>+.lock"で生成します。
SharedMemoryRepository#readonly(String name) | 排他する |
SharedMemoryRepository#writable(String name) | 排他する |
SharedMemoryRepository#copy(String name) | 排他しない |
課題
NativeAPIを触るとやはりallocateの煩わしさにいらだちます。allocateする際にbyte列を指定するのですが、CreateFileMapping関数ではサイズを64bitで指定できるのですが、32bitづつ分けて設定してくれという大胆な関数でした。 そんな変態的な仕様に対して、javaからはlong(8byte)をint(4byte)に分割して状態でJNIに引き渡すようにしてみた訳ですが、本当に正しいかが疑問に残ります。 ほかにも色々課題はあるのですが、とりあえずこれでcommit.
共有メモリを使う動機
私の部署で開発しているパッケージは基本的にjava(Swing)で開発している訳ですが、既存製品から移行し続ける経緯もあり、未だに帳票処理やバッチ処理ではCOBOLで開発しています。旧モデルの保守性を考えると、ビジネスロジック(COBOL)の資産がそのまま残ってしまったということです。
さて、javaからCOBOLを呼び出す際にはJava->JNI(C)->COBOLという破廉恥な連携が行われている訳ですが、たまにヘタクソなコボラーさんが、添字エラーを起こしちゃうようなプログラムを作っちゃうと、javaごとプロセスが死んでしまうという大惨事が引き起こされます。
なので、COBOLの処理をなんとかして別プロセスで動作させたいという動機が生まれた訳です。
でも、javaで抱えるメモリは共有したい!ということで、Google App EngineなんかであるようなMemcacheを、Windowsの共有メモリを使って実現したいということになったわけです。