POSIX 信号量
本节介绍 POSIX 信号量使用方法。
POSIX 信号量有两种类型:匿名信号量和命名信号量。匿名信号量只存在于内存中,这就要求使用信号量的线程必须可以访问内存,因此匿名信号量可以应用于同一进程中线程间的通信,不同的进程间需要映射这段内存到自己的地址空间。命名信号量可以通过名字访问,因此可以应用于进程间的通信。POSIX 信号量的本质是计数型信号量。
POSIX 信号量被定义为 sem_t 类型,使用前应该定义 sem_t 类型的变量,如:
sem_t sem;
POSIX 匿名信号量
一个 POSIX 匿名信号量必须要调用 sem_init 函数创建之后才能使用。
#include <semaphore.h>
int sem_init(sem_t *psem, int pshared, unsigned int value);
函数 sem_init 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 输出参数 psem 返回 POSIX 信号量的指针。
- 参数 pshared 标识了 POSIX 信号量是否进程共享(SylixOS 未用此项)。
- 参数 value 是 POSIX 信号量的初始值。
当一个 POSIX 匿名信号量使用完毕后(并确保以后也不再使用),应该调用 sem_destroy 函数删除它,SylixOS 会回收该信号量占用的内核资源。
#include <semaphore.h>
int sem_destroy(sem_t *psem);
函数 sem_destroy 原型分析:
- 此函数返回错误号。
- 参数 psem 是 POSIX 信号量的指针。
线程如果需要等待一个 POSIX 信号量,可以调用 sem_wait 函数,中断服务程序不能调用任何的 POSIX 信号量 API。
释放一个信号量使用 sem_post 函数。
#include <semaphore.h>
int sem_wait(sem_t *psem);
int sem_trywait(sem_t *psem);
int sem_timedwait(sem_t *psem, const struct timespec *abs_timeout);
int sem_reltimedwait_np(sem_t *psem, const struct timespec *rel_timeout);
以上几个函数原型分析:
- 函数成功返回 0,失败返回-1 并设置错误号。
- 参数 psem 是 POSIX 信号量的指针。
- 参数 abs_timeout 是等待的绝对超时时间。
- 参数 rel_timeout 是等待的相对超时时间。
sem_trywait 是 sem_wait 的“尝试等待”版本,在 POSIX 信号量的值为 0 时,sem_wait 将阻塞直至被唤醒,而 sem_trywait 将立即返回。
sem_timedwait 是 sem_wait 的带等待超时时间的版本,timeout 为等待的绝对超时时间,使用时在当前时间的基础上再加上一个相对超时时间就能得到绝对超时时间 timeout,如:
struct timespec ts;
clock_gettime(CLOCK_REALTIME, &ts);
ts.tv_sec += 1;
sem_timedwait(&sem, &ts);
sem_reltimedwait_np 是 sem_timedwait 的非 POSIX 标准版本,参数 rel_timeout 为等待的相对超时时间,如:
struct timespec ts;
ts.tv_sec = 1;
ts.tv_nsec = 0;
sem_reltimedwait_np(&sem, &ts);
可以看出在使用上,sem_reltimedwait_np 比 sem_timedwait 更方便。
#include <semaphore.h>
int sem_post(sem_t *psem);
函数 sem_post 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 psem 是 POSIX 信号量的指针。
下图显示了匿名信号量的基本操作函数在线程中的操作过程。
sem_getvalue 函数可以用来检索信号量值。需要注意的是,我们试图要使用刚读出来的值的时候,信号量值可能已经变化了,除非使用其他同步机制来避免这种竞争,否则 sem_getvalue 函数不建议使用。
#include <semaphore.h>
int sem_getvalue(sem_t *psem, int *pivalue);
函数 sem_getvalue 原型分析:
- 此函数成功返回 0,失败返回-1 并设置错误号。
- 参数 psem 是 POSIX 信号量的指针。
- 输出参数 pivalue 用于接收 POSIX 信号量的当前计数值。
下面程序展示了 POSIX 信号量的使用,程序创建两个线程和一个 POSIX 信号量,两个线程分别都对变量 count 进行自增操作和打印,使用 POSIX 信号量作为访问变量 count 的互斥手段。
#include <stdio.h>
#include <pthread.h>
#include <semaphore.h>
static sem_t lock;
static int count = 0;
static void *thread_a (void *arg)
{
while (1) {
sem_wait(&lock);
count++;
printf("thread_a(): count = %d\n", count);
sem_post(&lock);
usleep(500 * 1000);
}
return (NULL);
}
static void *thread_b (void *arg)
{
while (1) {
sem_wait(&lock);
count++;
printf("thread_b(): count = %d\n", count);
sem_post(&lock);
usleep(500 * 1000);
}
return (NULL);
}
int main (int argc, char *argv[])
{
pthread_t threada_tid;
pthread_t threadb_tid;
int ret;
ret = sem_init(&lock, 1, 1);
if (ret != 0) {
fprintf(stderr, "semaphore create failed.\n");
return (-1);
}
ret = pthread_create(&threada_tid, NULL, thread_a, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
ret = pthread_create(&threadb_tid, NULL, thread_b, NULL);
if (ret != 0) {
fprintf(stderr, "pthread create failed.\n");
return (-1);
}
pthread_join(threada_tid, NULL);
pthread_join(threadb_tid, NULL);
sem_destroy(&lock);
return (0);
}
在 SylixOS Shell 下运行程序:
# ./POSIX_Semaphore
thread_a(): count = 1
thread_b(): count = 2
thread_a(): count = 3
thread_b(): count = 4
thread_a(): count = 5
thread_b(): count = 6
......
# ts
NAME TID PID PRI STAT LOCK SAFE DELAY PAGEFAILS FPU CPU
---------------- ------- ----- --- ---- ---- ---- ---------- --------- --- ---
POSIX_Semaphore 401007b 38 200 JOIN 0 0 1 USE 0
pthread 401007c 38 200 SLP 0 138 0 0
pthread 401007d 38 200 SLP 0 138 0 0