只有0和1两种取值的信号量叫做二进制信号量,在这里将重点介绍。信号量此处主要是用于保护线程间的共享资源,使其每次只被一个执行线程获取。

在 Linux 中,这组 API 都以 sem_ 开头。

函数原型:


#include <semaphore.h>

int sem_init(sem_t *sem, int pshared, unsigned int value);

int sem_wait(sem_t *sem);

int sem_trywait(sem_t *sem);

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);

int sem_post(sem_t *sem);

int sem_destroy(sem_t *sem);

函数说明:

sem_init 用于初始化一个未命名的信号,其值是参数 value 决定的。而二个参数是用于描述这个信号是属于进程之间共享还是线程之间共享的。

返回值在正确的情况下为 0 ,不正确的情况下为 -1,并且设置 errno 的值。

sem_wait 的意思是等待一个信号量,当信号为 0 的时候则会阻塞,当信号量大于 0 的时候则会立即返回。

sem_trywait 则是不会返回的 sem_wait 函数,当信号值为 0 的时候则会立即返回 EAGAIN 的错误码。

sem_timedwait 则可以通过第二个参数设置阻塞的时间。

 struct timespec {
               time_t tv_sec;      /* Seconds */
               long   tv_nsec;     /* Nanoseconds [0 .. 999999999] */
           };

sem_post 该函数会解锁信号,这样做就会将 sem_wait 激活并由 sem_wait 再次锁定信号。

sem_destroy 字面意思,销毁 sem_init 创建出的信号。

例子:

这里使用一个 man 手册中比较直观的例子。

#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 <alarm-secs> <wait-secs>\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);
}