在Linux编程中,通过fork调用创建子进程从而实现多进程非常容易,应用也非常广泛。多进程之间要保护临界区资源,方法之一就是使用信号量。Linux中的信号量API有两套,一套是POSIX标准的API,另一套则是较老的System V API。POSIX API遵循POSIX标准,因此移植性更好,即便是非UNIX/Linux系统,只要支持POSIX标准,代码也可编译运行;而System V API存在时间更长,因此可能存在的更广泛。下面分别介绍这两种API。
POSIX信号量
POSIX信号量有两种形式:
- 命名信号量。命名信号量又一个名字标识,名字的形式为"/somename",即一个斜杠再加一些字母(只能又一个斜杠)。命名信号量可以用sem_open来创建或者打开,可以被多个进程同时操作。
- 匿名信号量。匿名信号量则没有名字标识,根据使用范围,分为线程间共享的匿名信号量和进程间共享的匿名信号量。进程间共享的匿名信号量通常放在全局变量中;而进程间共享的信号量则放在共享内存中。匿名信号量使用sem_init函数进行初始化。
POSIX信号量相关的API如下。
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,
mode_t mode, unsigned int value);
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_close(sem_t *sem);
int sem_unlink(const char *name);
int sem_destroy(sem_t *sem);
使用POSIX信号量的基本步骤为:
- 对于命名信号量,用sem_open创建或者打开,并指定名字。对于匿名信号量,应先申请sem_t存储,如果是进程间共享,可放到共享内存中;如果是线程间共享,则可放到全局变量中,然后调用sem_init函数初始化信号量。
- 调用sem_wait等待信号量。
- 访问临界区资源。
- 调用sem_post释放信号量。
- 对于命名信号量,调用sem_close释放信号量,并可调用sem_unlink删除。对于匿名信号量,调用sem_destroy删除信号量。
下面这个例子演示了命名信号量的使用。这个例子非常简单,父进程创建了两个子进程,然后三个进程都尝试获取信号量。从运行结果不难看出,由于初始值为1,因此刚开始两个进程尝试获取信号量只有1个成功,另一个等待;第一个获取信号量的进程释放后,后续进程才能再获取信号量。
/*
* posix_sem.c: demostrates POSIX named semaphore
*/
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <fcntl.h>
#include <semaphore.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#define SEM_NAME "/test_posix_shm"
static sem_