sem_wait - ライブラリコールの説明 - Linux コマンド集 一覧表
名前
sem_wait - セマフォをロックする
書式
#include <semaphore.h> int sem_wait(sem_t *sem);int sem_trywait(sem_t *sem); #define _XOPEN_SOURCE 600#include <semaphore.h> int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
説明
sem_wait
()は
sem
が指すセマフォの値を 1 減らす (ロックする)。
セマフォの値が 0 より大きい場合、減算が実行され、関数は直ちに復帰する。
セマフォの現在値が 0 の場合には、減算を実行できるようになる
(つまり、セマフォの値が 0 より大きな値になる) まで、もしくは
シグナルハンドラによって呼び出しが中断されるまで、
関数呼び出しは停止 (block) する。
sem_trywait
()は
sem_wait
()と同じだが、セマフォ値の減算をすぐに実行できなかった場合に、
停止 (block) するのではなくエラーで復帰する
(
errno
に
EAGAIN
がセットされる) 点が異なる。
sem_timedwait
()は
sem_wait
()と同じだが、セマフォ値の減算をすぐに実行できなかった場合に
関数呼び出しが停止する時間の上限を
abs_timeout
で指定する点が異なる。
abs_timeout
引き数は、タイムアウト時刻を指定する構造体へのポインタである。
この構造体には、タイムアウト時刻を時刻紀元 (Epoch; 1970 年 1 月 1 日
00:00:00) からの経過時間 (秒+ナノ秒) で指定する。
構造体は以下のように定義されている:
struct timespec { time_t tv_sec; /* Seconds */ long tv_nsec; /* Nanoseconds [0 .. 999999999] */ };
関数呼び出し時点ですでにタイムアウトに指定した時刻が過ぎており、
かつセマフォをすぐにロックできなかった場合は、
sem_timedwait
()はタイムアウトエラー
(
errno
に
ETIMEDOUT
がセットされる) で失敗する。
セマフォ操作がすぐに実行できるときは、
abs_timeout
がどんな値であっても
sem_timedwait
()が失敗することは決してない。さらにいうと、この場合には
abs_timeout
の正当性の検査は行われない。
返り値
成功すると、これらの関数は 0 を返す。 エラーの場合、セマフォの値を変更せずに、-1 を返し、 errno にエラーを示す値をセットする。
エラー
- EINTR
- 呼び出しはシグナルハンドラにより中断された。
- EINVAL
-
sem
は有効なセマフォでない。
sem_trywait ()の場合には、上記に加えて以下のエラーも起こる。
- EAGAIN
-
停止 (block) せずにロック操作を完了できなかった (つまり、
セマフォの現在の値が 0 であった)。
sem_timedwait ()の場合、以下のエラーも起こる。
- EINVAL
- abs_timeout.tv_nsecs の値が 0 未満、もしくは 1,000,000,000 以上である。
- ETIMEDOUT
- セマフォのロックに成功する前に時間切れとなった。
注意
シグナルハンドラは、 sigaction (2) の SA_RESTART フラグを使用しているかどうかに関わらず、これらの関数の呼び出しが 停止している場合、シグナルハンドラにより常に中断される。 POSIX.1-2001.
例
以下に示す (ちょっとした) プログラムは名前なしセマフォの操作を行う。 プログラムはコマンドライン引き数を 2 つ取る。 最初の引き数には、 SIGALRM シグナルを生成するためのアラームタイマーの設定に使われる値を 秒単位で指定する。このシグナルハンドラは、 main() 内で sem_timedwait ()を使って待っているセマフォを、 sem_post ()を使って加算する。 2番目の引き数には、 sem_timedwait ()に渡すタイムアウトまでの時間を秒単位で指定する。
$ ./a.out 2 3 About to call sem_timedwait() sem_post() from handler sem_getvalue() from handler; value = 1 sem_timedwait() succeeded $ ./a.out 2 1 About to call sem_timedwait() sem_timedwait() timed out
プログラムのソースコードは以下の通りである:
#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <semaphore.h> #include <time.h> #include <assert.h> #include <errno.h> #include <signal.h> #define die(msg) { perror(msg); exit(EXIT_FAILURE); } sem_t sem; static void handler(int sig) { int sval; printf("sem_post() from handler\n"); if (sem_post(&sem) == -1) die("sem_post"); if (sem_getvalue(&sem, &sval) == -1) die("sem_getvalue"); printf("sem_getvalue() from handler; value = %d\n", sval); } /* handler */ int main(int argc, char *argv[]) { struct sigaction sa; struct timespec ts; int s; assert(argc == 3); /* Usage: ./a.out alarm-secs wait-secs */ if (sem_init(&sem, 0, 0) == -1) die("sem_init"); /* Establish SIGALRM handler; set alarm timer using argv[1] */ sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGALRM, &sa, NULL) == -1) die("sigaction"); alarm(atoi(argv[1])); /* Calculate relative interval as current time plus number of seconds given argv[2] */ if (clock_gettime(CLOCK_REALTIME, &ts) == -1) die("clock_gettime"); ts.tv_sec += atoi(argv[2]); printf("main() about to call sem_timedwait()\n"); while ((s = sem_timedwait(&sem, &ts)) == -1 && errno == EINTR) continue; /* Restart when interrupted by handler */ /* Check what happened */ if (s == -1) { if (errno == ETIMEDOUT) printf("sem_timedwait() timed out\n"); else die("sem_timedwait"); } else printf("sem_timedwait() succeeded\n"); exit(EXIT_SUCCESS); }