共有ライブラリ上のデータをアタッチしているプロセス間で共有したいと思い色々と試行錯誤したが, 少しハマったので記事にしておきます.
共有ライブラリ上のグローバル変数
WindowsのDLLと異なりUNIX系の共有ライブラリはグローバル空間に変数を配置しても, プロセスごとのメモリ空間に配置されるため, 共有ライブラリを使用するだけではプロセス間通信は行なえません.
共有ライブラリで使用するデータを共有するには, 共有メモリを使用するのが最も容易です.
という訳でサンプルコードを示します.
# include <stdio.h>
# include <stdlib.h>
# include <errno.h>
# include <sys/shm.h>
static void *sharedmemory; // 共有メモリへのポインタ
static int seg_id; // 共有メモリID
static int* pnumattachproc;// 共有メモリにアタッチしているプロセス数(共有メモリ上に配置)
__attribute__ ((constructor))
void attach_shmem(void)
{
int isfirstproc = 0;
key_t key = 10;
size_t size = 1000;
// 共有メモリを作成
seg_id = shmget(key, size, IPC_CREAT | IPC_EXCL | 0660);
// 作成に失敗し, かつ同じキーの共有メモリが存在する場合
if ((seg_id == -1) && (errno == EEXIST)){
// その共有メモリのidを取得
seg_id = shmget(key, size, 0);
}else if(seg_id > 0){
// 共有メモリの作成に成功した場合は初期化処理を行うフラグを立てる
isfirstproc = 1;
}
if (seg_id == -1)
exit(1);
// 共有メモリをアタッチ
sharedmemory = shmat(seg_id, NULL, 0);
if(isfirstproc){
pnumattachproc = (int*)sharedmemory;
// アタッチしているプロセス数を初期化
*pnumattachproc = 0;
}
(*pnumattachproc)++; //アタッチしているプロセス数をインクリメント
}
__attribute__ ((destructor))
void detach_shmem(void)
{
(*pnumattachproc)--; //アタッチしているプロセス数をデクリメント
if(*pnumattachproc == 0){
//もしアタッチしているプロセス数が0になったら,
// 共有メモリを開放属性を付与しておく
shmctl(segid, IPC_RMID, NULL);
}
// 共有メモリデタッチ
(void) shmdt(sharedmemory);
}
int get_attachproc_num(void){
return *pnumattachproc;
}
となります. ビルドは共有ライブラリ用のオプションを使用します.
gcc -fPIC -shared -o libshm.so lib_shared_memory.c
注意点として,
shmctl(segid, IPC_RMID, NULL);
はアタッチしているプロセスが0になり開放が確定してから実行しましょう.
もしアタッチしているプロセスが存在している段階で実行してしまうと, 新たなプロセスが共有メモリにアタッチしようとした場合に, 同じキーにも関わらず新たな共有メモリを作成してしまいます.
これを防ぐために共有メモリにアタッチしているプロセス数を共有メモリ上に変数として置いて管理することで, 最後に共有ライブラリをデタッチするプロセスが開放できるようにしてあります.
私はこれを理解しておらず, 共有メモリ作成時に開放要求を追加しており, 共有メモリにアタッチすることができていませんでした.
以上です.