semop - システムコールの説明 - Linux コマンド集 一覧表
名前
semop, semtimedop - セマフォの操作
書式
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
int semop(int
semid
,
struct sembuf *
sops
,
unsigned
nsops
);
int semtimedop(int
semid
,
struct sembuf *
sops
,
unsigned
nsops
,
struct timespec *
timeout
);
説明
セマフォ集合 (semaphore set) のメンバーの各セマフォは 以下の関連情報を持っている:
unsigned short semval; /* セマフォ値 */ unsigned short semzcnt; /* ゼロを待つプロセス数 */ unsigned short semncnt; /* 増加を待つプロセス数 */ pid_t sempid; /* 最後に操作を行なったプロセス */
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
が 0 の場合、プロセスにはそのセマフォ集合に対する読み込み許可が
なければならない。
これは「ゼロまで待つ」動作である。
semval
が 0 ならば、操作は直ちに行われる。
semval
が 0 でない場合、
sem_flg
に
IPC_NOWAIT
が指定されていれば、
semop
()は失敗し、
errno
に
EAGAIN
が設定される (このとき
sops
の操作は全く実行されない)。
sem_flg
に
IPC_NOWAIT
が指定されていない場合、
semzcnt
(セマフォ値が 0 になるのを待っているプロセスの数) を 1 増加させて、
以下のいずれかが起こるまでプロセスを停止 (sleep) する
semval
が 0 になった: このとき
semval
の値は 1 減算される。
セマフォ集合が削除された: このとき
semop
()は失敗し、
errno
に
EIDRM
が設定される。
呼び出し元プロセスがシグナルを捕獲した: このとき
semzcnt
の値は 1 減算され、
semop
()は失敗し
errno
に
EINTR
が設定される。
semtimedop
()の
timeout
で指定された制限時間が経過した: このとき
semtimedop
()は失敗し、
errno
に
EAGAIN
が設定される。
sem_op
が 0 未満の場合、プロセスにはそのセマフォ集合を変更する許可がなければ
ならない。
semval
が
sem_op
の絶対値以上の場合は、操作は直ちに実行される:
semval
から
sem_op
の絶対値が減算される。
さらに、この操作に
SEM_UNDO
が指定されている場合は、このセマフォのプロセス・アンドゥ数
(
semadj
)を更新する。
semval
が
sem_op
の絶対値より小さく、
sem_flg
に
IPC_NOWAIT
が指定された場合は、
semop
()は失敗し、
errno
に
EAGAIN
が設定される (このとき
sops
の操作は全く実行されない)。
IPC_WAIT
が指定されていなければ、
semncnt
(このセマフォの値が増加するのを待っているプロセス数のカウンタ)
を 1 増加させて、以下のいずれかが起こるまでプロセスを停止 (sleep) する:
semval
が
sem_op
の絶対値以上になった: このとき
semncnt
が 1 減算され、
semval
から
sem_op
の絶対値が引かれる。
この操作に
SEM_UNDO
が指定されていた場合にはこのセマフォのプロセス・アンドゥ数
(
semadj
)も更新する。
セマフォ集合がシステムから削除された: このとき
semop
()は失敗し
errno
に
EIDRM
が設定される。
呼び出したプロセスがシグナルを捕獲した: このとき
semncnt
が 1 減算され、
semop
()は失敗し
errno
に
EINTR
が設定される。
semtimedop
()の
timeout
で指定された制限時間が経過した: このとき
semtimedop
()は失敗し、
errno
に
EAGAIN
が設定される。
操作が成功した場合、 sops が指す配列によって操作対象となった各セマフォの sempid メンバーには呼び出したプロセスのプロセス ID が設定される。 さらに sem_otime に現在時刻が設定される。
semtimedop ()関数の振る舞いは semop ()と全く同じだが、 呼び出し元プロセスが停止する場合、停止期間の上限が timeout 引き数の指す timespec 構造体で指定された時間となる点だけが異なる。 指定した制限時間に達した場合は、 semtimedop ()は失敗し、 errno に EAGAIN が設定される (このとき sops の操作は実行されない)。 timeout 引き数が NULL の場合、 semtimedop ()関数の振る舞いは semop ()関数と全く同じになる。
返り値
成功した場合、 semop ()と semtimedop ()は 0 を返す。そうでなければ -1 を返し、 エラーを示す errno を設定する。
エラー
失敗した場合、 errno に以下のどれかが設定される:
- E2BIG
- nsops 引き数が SEMOPM より大きい。 SEMOPM は一回のシステムコールで許される操作の最大個数である。
- EACCES
- 呼び出し元プロセスには指定されたセマフォ操作を行うのに 必要なアクセス許可がなく、 CAP_IPC_OWNER ケーパビリティもない。
- EAGAIN
- 操作を直ちに処理することができず、かつ sem_flg に IPC_NOWAIT が指定されているか timeout で指定された制限時間が経過した。
- EFAULT
- sops または timeout が指しているアドレスにアクセスできない。
- EFBIG
- ある操作で、 sem_num の値が 0 未満か、集合内のセマフォの数以上である。
- EIDRM
- セマフォ集合が削除された。
- EINTR
- このシステムコールで停止している時にプロセスがシグナルを捕獲した。
- EINVAL
- セマフォ集合が存在しないか、 semid が 0 未満であるか、 nsops が正の数でない。
- ENOMEM
- ある操作で sem_flg に SEM_UNDO が指定されたが、システムにアンドゥ構造体に割り当てる十分なメモリがない。
- ERANGE
- ある操作で sem_op+semval が SEMVMX より大きい。 SEMVMX は semval の最大値で、その値は実装依存である。
注意
あるプロセスの sem_undo 構造体は fork (2) システムコールの場合には子プロセスには継承されないが、 execve (2) システムコールの場合は継承される。
semop ()はシグナルハンドラによって中断された後に、 決して自動的に再開することはない。 たとえシグナルハンドラの設定時に SA_RESTART フラグがセットされていても再開することはない
semadj はプロセスごとの整数で、 SEM_UNDO フラグを設定して実行された全てのセマフォ操作の(負数の)カウンタである。 semctl (2) に SETVAL または SETALL を指定し、セマフォの値が 直接設定された場合には、全てのプロセスにおいて対応する semadj の値がクリアされる。
あるセマフォの semval , sempid , semzcnt , semnct の値はいずれも、適切な操作を指定して semctl (2) を呼び出すことで取得できる。
セマフォ集合のリソースに関する制限のうち、 semop ()に影響を及ぼすものを以下に挙げる:
- SEMOPM
- 一回の semop ()で許される操作の最大数 (32)。 (Linux では、この制限値は /proc/sys/kernel/sem の第3フィールドに対応し、読み出しも変更もできる)。
- SEMVMX
-
semval
が取り得る最大値: 実装依存 (32767)。
以下の値に関しては実装依存の制限はない。 終了時の調整 (adjust on exit) の最大値 ( SEMAEM )、システム全体のアンドゥ構造体の最大数 ( SEMMNU )、プロセスあたりのアンドゥ構造体の最大数。
semtimedop ()は Linux 2.5.52 で初めて登場し、 それからカーネル 2.4.22 にも移植された。
バグ
プロセスが終了する際、プロセスに対応する
semadj
の集合を使って、
SEM_UNDO
フラグ付きで実行された全てのセマフォ操作の影響を取り消す。
これによりある問題が発生する: これらのセマフォの調整を行っていると、
中にはセマフォの値が 0 未満の値にしようとする場合が出てくる。
このような場合、どのように実装するべきか?
ひとつの考えられる手法は、全てのセマフォ調整が実行されるまで
停止することである。しかし、この方法ではプロセスの終了が
長時間にわたって停止されることがあるので望ましくない。
しかもどれくらい長時間になるかは分からない。
別の可能性としては、このようなセマフォ調整を完全に無視してしまうことである。
(これはセマフォ操作として
IPC_NOWAIT
が指定するのと少し似ている)
Linux は第三の手法を採用している: セマフォの値を出来るだけ (つまり
0 まで)減少させて、プロセスの終了を直ちに続行できるようにしている。
カーネル 2.6.x (x <= 10) には、ある状況においてセマフォ値が 0 になるのを
待っているプロセスが、セマフォ値が実際に 0 になったときに起こされない、
というバグがある。このバグはカーネル 2.6.11 で修正されている。
準拠
SVr4, POSIX.1-2001.