kazmax - Linux で自宅サーバー

sem_wait - ライブラリコールの説明 - Linux コマンド集 一覧表

  1. 名前
  2. 書式
  3. 説明
  4. 返り値
  5. エラー
  6. 注意
  7. 関連項目

名前

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) するのではなくエラーで復帰する ( errnoEAGAIN がセットされる) 点が異なる。
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 ()はタイムアウトエラー ( errnoETIMEDOUT がセットされる) で失敗する。
セマフォ操作がすぐに実行できるときは、 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);
}

関連項目