今までに説明した 4 種類の同期プリミティブは、プロセスの境界を越えて使用するように設定できます。具体的には次のようにします。まず、その同期変数の領域が共有メモリーに確保されるようにします。次に、それぞれの初期化ルーチン (init) を呼び出すとき、引数 type に USYNC_PROCESS を指定します。
以上により、その同期変数に対する操作は、type が USYNC_THREAD のときとまったく同じように実行されます。
mutex_init(&m, USYNC_PROCESS, 0); rwlock_init(&rw, USYNC_PROCESS, 0); cond_init(&cv, USYNC_PROCESS, 0); sema_init(&s, count, USYNC_PROCESS, 0); |
プロセス間でロックと条件変数を使用する場合、必ずしもスレッドライブラリを使用しなければならないわけではありません。基本的にはスレッドライブラリを使用するものの、それが望ましくないときは、_lwp_mutex_* インタフェースと _lwp_cond_* インタフェースを次のようなやり方で使用するというアプローチを使用できます。
ロックと条件変数を通常どおり (shmop(2) または mmap(2) を使用して) 共有メモリーに確保します。
新たに割り当てられたオブジェクトを USYNC_PROCESS タイプとして初期化します。この初期化のために使用できるインタフェースはないので (_lwp_mutex_init(2) と _lwp_cond_init(2) は存在しない)、それらのオブジェクトは静的に割り当てて初期化したダミーオブジェクトを使って初期化します。
たとえば、lockp を初期化するには次のようにします。
lwp_mutex_t *lwp_lockp; lwp_mutex_t dummy_shared_mutex = SHAREDMUTEX; /* SHAREDMUTEX は /usr/include/synch.h の中で定義されている */ ... ... lwp_lockp = alloc_shared_lock(); *lwp_lockp = dummy_shared_mutex; |
同様に、条件変数については次のようにします。
lwp_cond_t *lwp_condp; lwp_cond_t dummy_shared_cv = SHAREDCV; /* SHAREDCV は /usr/include/synch.h の中で定義されている */ ... ... lwp_condp = alloc_shared_cv(); *lwp_condp = dummy_shared_cv; |
例 8-2 では、「生産者 / 消費者」問題の生産者と消費者をそれぞれ別のプロセスで表現しています。メインルーチンは、0 に初期化されたメモリーを自分のアドレス空間にマッピングし、それを子プロセスと共有します。mutex_init() と cond_init() を呼び出さなければならないのは、それらの同期変数のタイプが USYNC_PROCESS だからです。
子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。
この例では、生産者と消費者を呼び出す各駆動ルーチンも示しています。producer_driver() は stdin から文字を読み込み、producer() を呼び出します。consumer_driver() は consumer() を呼び出して文字を受け取り、stdout に書き出します。
例 8-2 のデータ構造は、条件変数による「生産者 / 消費者」のコーディング例のデータ構造と同じです (詳細は、「片方向リンクリストの入れ子のロック」を参照してください)。
main() { int zfd; buffer_t *buffer; zfd = open("/dev/zero", O_RDWR); buffer = (buffer_t *)mmap(NULL, sizeof(buffer_t), PROT_READ|PROT_WRITE, MAP_SHARED, zfd, 0); buffer->occupied = buffer->nextin = buffer->nextout = 0; mutex_init(&buffer->lock, USYNC_PROCESS, 0); cond_init(&buffer->less, USYNC_PROCESS, 0); cond_init(&buffer->more, USYNC_PROCESS, 0); if (fork() == 0) consumer_driver(buffer); else producer_driver(buffer); } void producer_driver(buffer_t *b) { int item; while (1) { item = getchar(); if (item == EOF) { producer(b, `¥0'); break; } else producer(b, (char)item); } } void consumer_driver(buffer_t *b) { char item; while (1) { if ((item = consumer(b)) == '¥0') break; putchar(item); } } |
子プロセスが 1 つ生成され、消費者の処理が実行されます。親プロセスは生産者の処理を実行します。