POSIX信号量与IPC信号量组中的信号量元素的逻辑完全一样,但POSIX信号量操作更加简便,接口更加易用。在多进程多线程中运用广泛。
POSIX信号量分成两种:
POSIX匿名信号量
通常用在线程间
只存在于内存,在文件系统中不可见
POSIX具名信号量
通常用在进程间
存在于文件系统/dev/shm中,可被不同进程操作
#include
sem_t s;
#include
int sem_init(sem_t *sem, int pshared, unsigned int value);
接口说明:
sem:待初始化信号量指针
pshared:指定信号量的作用范围
0:作用于进程内的线程间
非0:作用于进程间
value:信号量的初始值
示例
sem_t s;
int main()
{
// 初始化POSIX匿名信号量:
// 将其设定为在本进程内的个线程间使用
// 并将其初始值设定为1
sem_init(&s, 0, 1);
}
POSIX信号量(不管是匿名的还是具名的)的 P/V 操作相对于systemV的信号量组而言,接口非常简单:
#include
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作
接口说明:
P操作即申请资源,因此 sem_wait()在资源不足时会阻塞,V操作是释放资源,且V操作永远不会阻塞
示例
#include
sem_t s;
char buf[100];
void *routine(void *arg)
{
while(1)
{
// 申请信号量-1,输出字符串的长度 p操作
sem_wait(&s);
printf("%d\n", strlen(buf));
}
}
int main()
{
// 初始化,信号量初始值为0
sem_init(&s, 0, 0);
pthread_t t;
pthread_create(&t, NULL, routine, NULL);
while(1)
{
fgets(buf, 100, stdin);
// 输入字符串后,释放信号量+1 v操作
sem_post(&s);
}
}
POSIX 具名信号量主要用在多进程间同步互斥,其 P/V 操作与匿名版本无异,其最大的特点是存在于文件系统 /dev/shm 中,可以被系统中任意有权限的进程打开。
POSIX 具名信号量使用如下接口创建:
#include /* For O_* constants */
#include /* For mode constants */
#include
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag, mode_t mode, unsigned int value);
说明
与文件IO函数 open() 类似,sem_open() 也提供了两个版本,初建时需要指定文件权限 mode 和初始值 value,后续再打开使用时则无需指定。
示例
// 在进程 a.c 中创建具名信号量
int main()
{
// 创建有名信号量,参数1代表初始值为1
sem_t *s = sem_open("mysem", O_CREAT, 0666, 1);
}
// 在进程 b.c 中打开具名信号量
int main()
{
// 打开具名信号量
sem_t *s = sem_open("mysem", O_RDWR);
}
编译程序 a.c(注意要加线程库 -lpthread)并运行该程序,便会在系统 /dev/shm/ 产生对应的文件:
gec@ubuntu:~$ gcc a.c -o a -lpthread
gec@ubuntu:~$ ./a
gec@ubuntu:~$ ls -l /dev/shm/
总用量 4
-rw-rw-r-- 1 gec gec 32 Nov 26 18:27 sem.mysem
gec@ubuntu:~$
与匿名信号量完全一致:
#include
int sem_wait(sem_t *sem); // P操作
int sem_post(sem_t *sem); // V操作
示例
// a.c
int main()
{
sem_t *s = sem_open("mysem", O_CREAT, 0666, 0);
printf("A\n");
sem_post(s); // V操作
}
// b.c
int main()
{
sem_t *s = sem_open("mysem", O_RDWR);
// 等待A执行完毕,才执行B
sem_wait(s); // P操作
printf("B\n");
}
信号量的V操作就像一个远程开关,控制另一个进程的进度:在进程A尚未执行 sem_post(s) 之前,进程B会一直在 sem_wait(s) 静静等待,这就是多进程进度控制。
注意,上述程序 a.c 和程序 b.c 是两个独立的程序,彼此之间没有任何共享的变量和数据,他们通过基于文件系统的具名信号量达成协同。
POSIX 具名信号量跟文件操作非常类似,打开之后会在内核需要对其维护,因此在不再需要的时候应该予以关闭:
sem_close(s);
另外,即使所有进程都关闭了信号量并且退出,具名信号量对应的文件是不会消失的,并且会保留所有 P/V 操作后的值,如果不再需要这个文件本身,则除了可以直接在文件系统中删除外,也可以使用如下接口删除:
sem_unlink("mysem");
上文提到,具名信号量会将所有的 P/V 操作后的值,因此向上述程序 a.c 如果连续执行三遍,那么信号量的值将会被 +3 ,因此程序 b.c 可以连续进行三次 P 操作。