POSIX信号量
POSIX信号量总体上来说与System V相似,都是一个不小于0的整数
SUSv3规定了两种类型的POSIX信号量
-
命名信号量
该信号量拥有一个名字,可以使用相同的名字来调用sem_open(),这样不同进程之间就能访问同一个信号量
-
未命名信号量
该信号量没有名字,它位于内存中一个预先商定的位置中,它可以在进程和线程之间共享
当在进程之间共享时,信号量必须位于一个共享内存中
当在线程中共享时,信号量必须位于线程可共享的区域,即堆或者全局变量中
区别
这两种信号量的最大区别在于他们的持久性不同
-
对于命名信号量来说,他们具有内核持久性,只有当系统被关闭时,他们才会消失
Linux在
/dev/shm
文件下维护了这些信号量 -
对于为命名信号量来说,他们的持久性与它所处的共享内存的持久性有关,如果是全局变量或堆,那就是程序结束时对象消失
命名信号量
1.打开一个命名信号量
#include<fcntl.h>
#include<sys/stat.h>
#include<semaphore.h>
sem_t *sem_open(const char *name, in oflag, ...
/*mode_t mode, unsigned int value*/)
//成功:返回0 失败:返回SEM_FAILED
参数解释:
-
name
信号量的名字,必须以"/"开头
-
oflag
位掩码,可以选用O_CREAT和O_EXCL,当oflag被指定为0时,将访问一个已有信号量
-
mode
如果是创建新信号量之上的话,mode指定了位于新信号量之上的权限,会与umask取掩码
在Linux中,会将该权限默认为O_RDWR,这就需要确保使用到该信号量的进程对该信号量都必须具有读写权限
-
value
指定了新信号量的初始值
注意:在POSIX中信号的创建于初始化是原子的,与System V不同
通过fork()可以继承父进程打开的所有命名信号量的引用
2.信号量的关闭与删除
- sem_close()会断开进程与信号量之间的联系,释放系内核为进程关联到该信号量之上的所有资源,并且递减该引用信号量的进程数
#include<semaphore.h>
int sem_close(sem_t *sem);
//成功:返回 0 失败:返回-1
当一个进程打开了信号量并且终止或者使用了exec()时,该信号量会被自动关闭(close)
- sem_unlink()会将信号量标记为这样一种状态:当没有进程引用该信号量时删除该对象
#include<semaphore.h>
int sem_close(sem_t *sem);
//成功:返回 0 失败:返回-1
sem_close()与sem_unlink()的区别:
close()只是断开进程与信号量之间的联系,当所有打开信号量的进程都执行了close()调用之后,信号量也不会被删除
只有当调用了sem_unlink()并且没有进程引用该对象时,信号量才会被真正删除
3.信号量的等待(wait),发布(post),获取(get)
POSIX对于信号量的修改一次性只能修改1,不能超过这个数字,因为POSIX只提供了修改1的API
-
sem_wait()函数会将sem引用的信号量的值减1
#include<semaphore.h> int sem_wait(sem_t *sem); //成功: 返回0 失败: 返回-1
如果sem的值大于0则会立即返回,否则陷入阻塞
如果sem_wait()调用被一个信号处理器中断了,那么它就会失败并且返回EINTR,还有一个特别的点就是如果在sigaction()指定了SA_RESTART标志也是会被忽略的
-
sem_trywait()会进行非阻塞版本的wait
#include<semaphore.h> int sem_trywait(sem_t *sem); //成功:返回0 失败:返回-1
如果sem的值等于0,则会立即失败,返回EAGAIN, 不会阻塞
-
sem_timewait()函数会对wait()操作的阻塞时间进行限制
#define _XOPEN_SOURCE 600 #include<semaphore.h> int sem_timewait(sem_t *sem, const struct timespec *abs_timeout) //成功:返回0 失败:返回-1
abs_timeout应该是一个自Epoch以来的时间,如果想使用相对时间,应该使用
clock_gettime()
获取到CLOCK_REALTIME时钟的当前值再加上相对时间 -
sem_post()将sem引用的信号量加1
#include<semaphore.h> int sem_post(sem_t *sem); //成功:返回0 失败:返回-1
-
sem_getval()可以获取到信号量sem的当前值
#include<semaphore.h> int sem_getvalue(sem_t *sem, int *sval); //成功:返回0 失败:返回-1
该系统调用会将sem指定的信号量的值返回到sval指定的区域
当多个进程正在阻塞wait()时,Linux中使用sem_getvalue()会返回0,其他实现中可能会返回等待者数目的相反数
未命名信号量
对于未命名信号量来说,命名信号量所使用的API对于他们来说也可以使用,但是他们有着自己独有的APIsem_init()和sem_destory()
-
sem_init()用来初始化一个未命名信号量
#include<semaphore.h> int sem_int(sem_t *sem, int pshared, unsigned int value); //成功:返回0 失败:返回-1
参数解释:
-
sem
信号量标识符
-
pshared
该参数用来指定信号量是在进程间共享还是在线程间共享
- 如果pshared为0,那么是在线程间共享,sem是共享区域(全局变量,堆)中某个位置的内存地址,会在进程终止时被销毁
- 如果pshared不为0,那么是在进程之间共享,sem必须是某个共享内存区域(共享内存对象,mmap()创建的共享映射)的地址
-
value
初始值
不能够对同一个信号量init两次
-
-
sem_destory()用于销毁一个未命名信号量
#include<semaphore.h> int sem_destory(sem_t *sem); //成功:返回0 失败:返回-1
destory()操作应该在底层内存被释放之前进行,当destory()一个未命名信号量之后,就可以对其重新init