sem_wait

sem_wait

sem_wait是一個函式,也是一個原子操作,它的作用是從信號量的值減去一個“1”,但它永遠會先等待該信號量為一個非零值才開始做減法。也就是說,如果你對一個值為2的信號量調用sem_wait(),執行緒將會繼續執行,將信號量的值將減到1。 如果對一個值為0的信號量調用sem_wait(),這個函式就會原地等待直到有其它執行緒增加了這個值使它不再是0為止。如果有兩個執行緒都在sem_wait()中等待同一個信號量變成非零值,那么當它被第三個執行緒增加 一個“1”時,等待執行緒中只有一個能夠對信號量做減法並繼續執行,另一個還將處於等待狀態。sem_trywait(sem_t *sem)是函式sem_wait的非阻塞版,它直接將信號量sem減1,同時返回錯誤代碼。

描述

sem_wait() 減小(鎖定)由sem指定的信號量的值.如果信號量的值比0大,
那么進行減一的操作,函式立即返回.
如果信號量當前為0值,那么調用就會一直阻塞直到或者是信號量變得可以進行減一的操作
(例如,信號量的值比0大),或者是信號處理程式中斷調用

sem_trywait() 和 sem_wait()是一樣的,除了如果不能夠對信號量立即進行減一,
那么sem_trywait()就會返回一個錯誤(錯誤號是AGAIN)而不是鎖定.
sem_timedwait() 和 sem_wait()是一樣的,除了如果減一操作不能立即執行的話,
abs_timeout 指定了調用應該被阻塞的時間限制.
abs_timeout參數指向了一個結構體指定了由秒和納秒組成的絕對的逾時值:
從1970-01-01 00:00:00 +0000紀元開始的UTC,結構體的定義如下:
struct timespec
{
time_t tv_sec; /* Seconds */
long tv_nsec; /* Nanoseconds [0 .. 999999999] */
};
如果逾時值已經超過了調用規定的值,那么信號量不能被立即鎖定,
之後sem_timedwait() 為逾時失敗(error設定為ETIMEDOUT).

如果操作立即生效,那么sem_timedwait() 永遠不會返回逾時的錯誤,不管abs_timeout的值.
更進一步的是,在這種情況下abs_timeout值的有效性都不會檢查.

返回值

所有的函式成功返回0,錯誤的話信號量的值不改動,返回-1.errno設定來標識錯誤.

錯誤

EINTR The call was interrupted by a signal handler; see signal(7).
//調用被信號處理中斷

EINVAL sem is not a valid semaphore.
//sem不是有效的信號量

The following additional error can occur for sem_trywait():
//下面的錯誤是sem_trywait()可能發生的:

EAGAIN The operation could not be performed without blocking (i.e., the
semaphore currently has the value zero).
//除了鎖定無法進行別的操作(如信號量當前是0值).

The following additional errors can occur for sem_timedwait():
//下面的錯誤是sem_timedwait()可能發生的:

EINVAL The value of abs_timeout.tv_nsecs is less than 0, or greater than or
equal to 1000 million.
//abs_timeout.tv_nsecs 的值比0小或者大於等於1000毫秒(譯者注:納秒的值不能比0小,不能比1秒大)

ETIMEDOUT
The call timed out before the semaphore could be locked.
//在信號量鎖定之前就逾時了

注意

對這些函式,信號處理程式總是會中斷阻塞,不管是否使用了sigaction(2)的SA_RESTART標誌位.

範例

//(有些瑣碎的)程式以下展示了在一個未命名的信號量上的操作.程式請求2個命令行參數,
//第一個參數指定一個秒的參數來作為報警的定時器來產生SIGALRM信號.
//信號處理程式執行sem_post(3)來增加在main()函式中使用sem_wait()等待的信號量的值.
//第二個命令行參數指定逾時的長度,為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
Program source
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <semaphore.h>
#include <time.h>
#include <assert.h>
#include <errno.h>
#include <signal.h>
sem_t sem;
#define handle_error(msg) /
do

{

perror(msg);

exit(EXIT_FAILURE);

} while (0)
static void
handler(int sig)
{
write(STDOUT_FILENO, "sem_post() from handler/n", 24);
if (sem_post(&sem) == -1)
{
write(STDERR_FILENO, "sem_post() failed/n", 18);
_exit(EXIT_FAILURE);
}
}
int
main(int argc, char *argv[])
{
struct sigaction sa;
struct timespec ts;
int s;
if (argc != 3)
{
fprintf(stderr, "Usage: %s /n",
argv[0]);
exit(EXIT_FAILURE);
}
if (sem_init(&sem, 0, 0) == -1)
handle_error("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)
handle_error("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)
handle_error("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 if interrupted by handler */
/* Check what happened */
if (s == -1)
{
if (errno == ETIMEDOUT)
printf("sem_timedwait() timed out/n");
else
perror("sem_timedwait");
}
else
printf("sem_timedwait() succeeded/n");
exit((s == 0) ? EXIT_SUCCESS : EXIT_FAILURE);
}

相關詞條

熱門詞條

聯絡我們