#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); int semtimedop(int semid, struct sembuf *sops, size_t nsops, const struct timespec *timeout);
glibc 向けの機能検査マクロの要件 (feature_test_macros(7) 参照):
unsigned short semval; /* semaphore value */ unsigned short semzcnt; /* # waiting for zero */ unsigned short semncnt; /* # waiting for increase */ pid_t sempid; /* PID of process that last
semop() は semid で指定されたセマフォ集合の選択されたセマフォに対して操作を行う。 sops は nsops 個の要素の配列を指し、配列の各要素は個々のセマフォに対する操作を示す構造体である。その型は struct sembuf で、次のメンバを持つ:
unsigned short sem_num; /* セマフォ番号 */ short sem_op; /* セマフォ操作 */ short sem_flg; /* 操作フラグ */
sem_flg には IPC_NOWAIT と SEM_UNDO が設定できる。 SEM_UNDO が指定された操作は、そのプロセスが終了した時に自動的に取り消される。
sops に含まれる操作の集合は、 配列の順序 で、 アトミックに 実行される。 すなわち、全ての操作が完全に実行されるか、全く実行されないかの どちらかとなる。 全ての操作が直ちに実行できない場合のこのシステムコールの振る舞いは 個々の操作の sem_flg フィールドに IPC_NOWAIT が存在するかによって決まり、後述のようになる。
それぞれの操作はセマフォ集合の sem_num番目 のセマフォに対して実行される。セマフォ集合の最初のセマフォには 番号 0 が振られる。 そして操作は三種類あり、 sem_op の値で区別される。
sem_op が正の整数の場合、その値をセマフォの値 (semval) に加算する。 さらに、この操作で SEM_UNDO が指定されていた場合は、 システムはこのセマフォのの調整値 (semadj) から値 sem_op を減算する。 この操作は必ず実行でき、 スレッドの停止は起こらない。 呼び出し元プロセスは対象のセマフォ集合を変更する許可がなければならない。
sem_op が 0 の場合、「ゼロまで待つ」操作である。この場合、プロセスは そのセマフォ集合に対する読み込み許可がなければならない。 semval が 0 ならば、操作は直ちに行われる。 semval が 0 でない場合、 sem_flg に IPC_NOWAIT が指定されていれば、 semop() は失敗し、 errno に EAGAIN が設定される (このとき sops に対する操作は全く実行されない)。 sem_flg に IPC_NOWAIT が指定されていない場合、 semzcnt (セマフォ値が 0 になるのを待っているスレッドの数) を 1 増加させて、 以下のいずれかが起こるまでスレッドを停止 (sleep) する。
sem_op が 0 未満の場合、プロセスにはそのセマフォ集合を変更する許可がなければ ならない。 semval が sem_op の絶対値以上の場合は、操作は直ちに実行される: semval から sem_op の絶対値を減算し、さらに、この操作に SEM_UNDO が指定されている場合は、このセマフォの調整値 (semadj) に sem_op の絶対値を加算する。 semval が sem_op の絶対値より小さく、 sem_flg に IPC_NOWAIT が指定された場合は、 semop() は失敗し、 errno に EAGAIN が設定される (このとき sops の操作は全く実行されない)。 semval が sem_op の絶対値より小さく、 IPC_WAIT が指定されていない場合は、 semncnt (このセマフォの値が増加するのを待っているスレッド数のカウンター) を 1 増加させて、以下のいずれかが起こるまでスレッドを停止 (sleep) する。
操作が成功した場合、 sops が指す配列によって操作対象となった各セマフォの sempid メンバーには呼び出し元のプロセス ID が設定される。さらに sem_otime に現在時刻が設定される。
semtimedop() がシグナルにより割り込まれた場合、呼び出しはエラー EINTR で失敗し、 timeout の内容は変更されないままとなる点に注意すること。
あるプロセスの sem_undo 構造体は fork(2) で生成された子プロセスには継承されないが、 execve(2) システムコールの場合は継承される。
semop() はシグナルハンドラーによって中断された後に、 決して自動的に再開することはない。 たとえシグナルハンドラーの設定時に SA_RESTART フラグがセットされていても再開することはない
セマフォの調整値 (semadj) は、プロセス毎のセマフォ毎の整数で、 SEM_UNDO フラグを指定して行われた、セマフォに対するすべての操作の合計値を反転したものである。 各プロセスは semadj の値のリストを保持する --- リストのそれぞれの値は SEM_UNDO を使って操作が行われた個々のセマフォに対応する。 プロセスが終了する際、 セマフォ毎の semadj の各々の値が対応するセマフォに加算される。 これにより、そのプロセスがそのセマフォに対して行った操作の影響が取り消される (ただし、下記の「バグ」を参照)。 semctl(2) の SETVAL や SETALL を使ってセマフォの値が直接設定された場合、 すべてのプロセスの対応する semadj の値がクリアされる。 clone(2) の CLONE_SYSVSEM フラグを使うと、 複数のプロセスがひとつの semadj リストを共有できる。 詳細は clone(2) を参照。
あるセマフォの semval, sempid, semzcnt, semnct の値はいずれも、適切な操作を指定して semctl(2) を呼び出すことで取得できる。
以下の値に関しては実装依存の制限はない。 終了時の調整 (adjust on exit) の最大値 (SEMAEM)、 システム全体のアンドゥ構造体の最大数 (SEMMNU)、 プロセスあたりのアンドゥ構造体の最大数。
カーネル 2.6.x (x <= 10) には、ある状況においてセマフォ値が 0 になるのを 待っているスレッドが、セマフォ値が実際に 0 になったときに起床 (wake up) されない、というバグがある。このバグはカーネル 2.6.11 で修正されている。
struct sembuf sops[2]; int semid;
/* Code to set semid omitted */
sops[0].sem_num = 0; /* Operate on semaphore 0 */ sops[0].sem_op = 0; /* Wait for value to equal 0 */ sops[0].sem_flg = 0;
sops[1].sem_num = 0; /* Operate on semaphore 0 */ sops[1].sem_op = 1; /* Increment value by one */ sops[1].sem_flg = 0;
if (semop(semid, sops, 2) == -1) {
perror("semop");
exit(EXIT_FAILURE); }
A further example of the use of semop() can be found in shmop(2).
[man1]
[man2]
[man3]
[man4]
[man5]
[man6]
[man7]
[man8]
[a]
[b]
[c]
[d]
[e]
[f]
[g]
[h]
[i]
[j]
[k]
[l]
[m]
[n]
[o]
[p]
[q]
[r]
[s]
[t]
[u]
[v]
[w]
[x]
[y]
[z]