セマフォーを使用すると、プロセスは状態情報を問い合わせたり、変更したりできます。通常、セマフォーは共有メモリーセグメントなどのシステム資源が利用可能かどうかを監視して制御するために使用します。セマフォーは、個々のユニットまたはセット内の要素として操作できます。
System V IPC セマフォーは、大きな配列の中に存在できるため、極めて重いセマフォーです。より軽量なセマフォーは、スレッドライブラリで利用できます。また、POSIX セマフォーは System V セマフォーの最新の実装です (「POSIX セマフォー」を参照)。スレッドライブラリセマフォーは、マッピングされたメモリーで使用する必要があります (「メモリー管理インタフェース」を参照)。
セマフォーのセットは、制御構造体と個々のセマフォーの配列からできており、デフォルトでは、25 個までの要素を持つことができます。セマフォーのセットは、semget(2) を使用して初期化する必要があります。セマフォー作成者は semctl(2) を使用して、その所有権またはアクセス権を変更でき、アクセス権を持つプロセスは、semctl(2) を使用して操作を制御できます。
セマフォー操作は semop(2) によって行います。このインタフェースは、セマフォー操作構造体の配列へのポインタを受け入れます。操作配列内の各構造体は、セマフォーに実行する操作についてのデータを持ちます。読み取り権を持つプロセスは、セマフォーがゼロ値を持っているかどうかを検査できます。セマフォーを増分または減分する操作には、書き込み権が必要です。
操作が失敗すると、どのセマフォーも変更されません。IPC_NOWAIT フラグが設定されている場合を除いて、プロセスはブロックし、次のいずれかになるまでブロックされたままです。
セマフォー操作がすべて終了して呼び出しが成功した。
プロセスがシグナルを受信した。
セマフォーのセットが削除された。
セマフォーを更新できるのは、一度に 1 つのプロセスだけです。異なるプロセスが同時に要求した場合は、任意の順序で処理されます。操作の配列が semop(2) 呼び出しによって与えられると、配列内のすべての操作が正常に終了できるまで更新されません。
セマフォーを排他的に使用しているプロセスが異常終了し、操作の取り消しまたはセマフォーの解放に失敗した場合、セマフォーはメモリー内にロックされたままになります。この現象を防ぐには semop(2) に SEM_UNDO 制御フラグを指定して、各セマフォー操作に undo 構造体を割り当て、セマフォーを以前の状態に戻すことができるようにします。プロセスが異常終了すると、undo 構造体内の操作がシステムによって適用されます。これにより、プロセスが異常終了しても、セマフォーの整合性が保たれます。
プロセスがセマフォーによって制御される資源へのアクセスを共有する場合は、SEM_UNDO を有効にしてセマフォーに対する操作を行わないでください。現在、資源を制御しているプロセスが異常終了すると、その資源は整合性のない状態になったと見なされます。別のプロセスがこの資源を整合性のある状態に復元するためには、そのことを認識できるようにする必要があります。
SEM_UNDO を有効にしてセマフォー操作を実行するときは、取り消し操作を行う呼び出しについても SEM_UNDO を有効にしておく必要があります。プロセスが正常に実行されると、取り消し操作は undo 構造体に補数値を補って更新します。このため、プロセスが異常終了しない限り、undo 構造体に適用された値は最終的に取り消されて 0 になります。undo 構造体は 0 になると削除されます。
SEM_UNDO を正しく使用しないと、割り当てられた undo 構造体がシステムをリブートするまで解放されないため、メモリーリークが発生する可能性があります。
semget(2) は、セマフォーの初期化またはセマフォーへのアクセスを行います。呼び出しが成功すると、セマフォー ID (semid) を返します。key 引数は、セマフォー ID に関連付けられた値です。nsems 引数は、セマフォー配列内の要素数を指定します。nsems が既存の配列の要素数を超えると呼び出しは失敗します。正しい数がわからない場合は、nsems 引数を 0 に指定すると正しく実行されます。semflg 引数は、初期状態のアクセス権と作成の制御フラグを指定します。
SEMMNI システム構成オプションは、配列内のセマフォーの最大数を指定します。SEMMNS オプションは、すべてのセマフォーのセットを通じて個々のセマフォーの最大数を指定します。ただし、セマフォーのセット間の断片化のため、利用できるすべてのセマフォーを割り当てられない場合もあります。
次のコードに、semget(2) の使用例を示します。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> ... key_t key; /* key to pass to semget() */ int semflg; /* semflg to pass to semget() */ int nsems; /* nsems to pass to semget() */ int semid; /* return value from semget() */ ... key = ... nsems = ... semflg = ... ... if ((semid = semget(key, nsems, semflg)) == –1) { perror("semget: semget failed"); exit(1); } else exit(0); ...
semctl(2) は、セマフォーのセットのアクセス権とその他の特性を変更します。semctl(2) では、有効なセマフォー ID を指定して呼び出してください。semnum 値は、そのインデックスによって配列内のセマフォーを選択します。cmd 引数は、次のいずれかの制御フラグです。
単一セマフォーの値を戻します。
単一セマフォーの値を設定します。この場合、arg は int 値の arg.val と解釈されます。
セマフォーまたは配列に対して最後に操作を実行したプロセスの PID を戻します。
セマフォーの値が増加するのを待っているプロセス数を戻します。
特定のセマフォーの値が 0 に達するのを待っているプロセス数を戻します。
セット内のすべてのセマフォーの値を戻します。この場合、arg は unsigned short 値の配列へのポインタである arg.array と解釈されます。
セット内のすべてのセマフォーに値を設定します。この場合、arg は unsigned short 値の配列へのポインタである arg.array と解釈されます。
制御構造体からセマフォーのセットの状態情報を取得し、semid_ds 型のバッファーへのポインタ arg.buf が指すデータ構造体に入れます。
有効なユーザーおよびグループの識別子とアクセス権を設定します。この場合、arg は arg.buf と解釈されます。
指定したセマフォーのセットを削除します。
IPC_SET または IPC_RMID コマンドを実行するには、所有者、作成者、またはスーパーユーザーとして有効なユーザー識別子を持つ必要があります。その他の制御コマンドには、読み取り権と書き込み権が必要です。
次のコードに、semctl(2) の使用例を示します。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> ... register int i; ... i = semctl(semid, semnum, cmd, arg); if (i == –1) { perror("semctl: semctl failed"); exit(1); ...
semop(2) は、セマフォーのセットへの操作を実行します。semid 引数は、以前の semget(2) 呼び出しによって戻されたセマフォー ID です。sops 引数は、セマフォー操作について次のような情報を含む構造体の配列へのポインタです。
セマフォー番号
実行する操作
制御フラグ (存在する場合)
sembuf 構造体は、sys/sem.h に定義されているセマフォー操作を指定します。nsops 引数は配列の長さを指定します。配列の最大長は、SEMOPM 構成オプションで指定されます。このオプションでは、単一の semop(2) 呼び出しで使用できる最大操作数が決定され、デフォルトではその値は 10 に設定されています。
実行する操作は、次のように判断されます。
正の整数の場合は、セマフォーの値をその数だけ増加します。
負の整数の場合は、セマフォーの値をその数だけ減少します。セマフォーを 0 未満の値に設定しようとすると、IPC_NOWAIT が有効であるかどうかによって、失敗するかブロックされます。
値が 0 の場合は、セマフォーの値が 0 になるのを待ちます。
semop(2) で使用できる制御フラグは IPC_NOWAIT と SEM_UNDO の 2 つです。
配列内のどの操作についても設定できます。IPC_NOWAIT が設定されている操作を実行できなかった場合、セマフォーの値を変更せずにインタフェースを戻します。セマフォーを現在の値より多く減らそうしたり、セマフォーが 0 でないときに 0 かどうか検査しようとするとインタフェースは失敗します。
プロセスの終了時に配列内の個々の操作を取り消します。
次のコードに、semop(2) の使用例を示します。
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> ... int i; /* work area */ int nsops; /* number of operations to do */ int semid; /* semid of semaphore set */ struct sembuf *sops; /* ptr to operations to perform */ ... if ((i = semop(semid, sops, nsops)) == –1) { perror("semop: semop failed"); } else (void) fprintf(stderr, "semop: returned %d\n", i); ...