二.System V信号量和Posix信号量区别
信号量有两种实现:传统的System V信号量和新的POSIX信号量。它们所提供的函数很容易被区分:对于所有System V信号量函数,在它们的名字里面没有下划线。例如,应该是semget()而不是sem_get()。然而,所有的的POSIX信号量函数都有一个下划线。下面列出了它们提供的所有函数清单:
Systm V POSIX
semctl() sem_getvalue()
semget() sem_post()
semop() sem_timedwait()
sem_trywait()
sem_wait()
sem_destroy()
sem_init()
sem_close()
sem_open()
sem_unlink()
另外一个区别是,对于POSIX信号量,你可以有命名的信号量,例如,信号量有一个文件
关联它们,
对于最后三个函数,被用来创建,关闭和删除这样一个命名的信号量。
而sem_init()和sem_destroy()仅仅供非命名信号量使用。
他们是有关信号量的两组程序设计接口函数。POSIX信号量来源于POSIX技术规范的实时
扩展方案(POSIX Realtime Extension),常用于线程;system v信号量,常用于进程的同步。
这两者非常相近,但它们使用的函数调用各不相同。前一种的头文件为semaphore.h,函数
调用为sem_init(),sem_wait(),sem_post(),sem_destory()等等。后一种头文件为<sys/sem.h>,
函数调用为semctl(),semget(),semop()等函数。
---------------------------------------------------------我是分割线--------------------------------------------------------
信号量函数由semget、semop、semctl三个函数组成。下面的表格列出了这三个函数的函数原型及具体说明。
- semget函数原型
semget(得到一个信号量集标识符或创建一个信号量集对象)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数说明
得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符
函数原型
int semget(key_t key, int nsems, int semflg)
函数传入值
key
0(IPC_PRIVATE):会建立新信号量集对象
大于0的32位整数:视参数semflg来确定操作,通常要求此值来源于ftok返回的IPC键值
nsems
创建信号量集中信号量的个数,该参数只在创建信号量集时有效
msgflg
0:取信号量集标识符,若不存在则函数会报错
IPC_CREAT:当semflg&IPC_CREAT为真时,如果内核中不存在键值与key相等的信号量集,则新建一个信号量集;如果存在这样的信号量集,返回此信号量集的标识符
IPC_CREAT|IPC_EXCL:如果内核中不存在键值与key相等的信号量集,则新建一个消息队列;如果存在这样的信号量集则报错
函数返回值
成功:返回信号量集的标识符
出错:-1,错误原因存于error中
附加说明
上述semflg参数为模式标志参数,使用时需要与IPC对象存取权限(如0600)进行|运算来确定信号量集的存取权限
错误代码
EACCESS:没有权限
EEXIST:信号量集已经存在,无法创建
EIDRM:信号量集已经删除
ENOENT:信号量集不存在,同时semflg没有设置IPC_CREAT标志
ENOMEM:没有足够的内存创建新的信号量集
ENOSPC:超出限制
如果用semget创建了一个新的信号量集对象时,则semid_ds结构成员变量的值设置如下:
Ÿ sem_otime设置为0。
Ÿ sem_ctime设置为当前时间。
Ÿ msg_qbytes设成系统的限制值。
Ÿ sem_nsems设置为nsems参数的数值。
Ÿ semflg的读写权限写入sem_perm.mode中。
Ÿ sem_perm结构的uid和cuid成员被设置成当前进程的有效用户ID,gid和cuid成员被设置成当前进程的有效组ID。
- semop函数原型
semop(完成对信号量的P操作或V操作)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数说明
对信号量集标识符为semid中的一个或多个信号量进行P操作或V操作
函数原型
int semop(int semid, struct sembuf *sops, unsigned nsops)
函数传入值
semid:信号量集标识符
sops:指向进行操作的信号量集结构体数组的首地址,此结构的具体说明如下:
struct sembuf {
short semnum; /*信号量集合中的信号量编号,0代表第1个信号量*/
short val;/*若val>0进行V操作信号量值加val,表示进程释放控制的资源 */
/若val<0进行P操作信号量值减val,若(semval-val)<0(semval为该信号量值),则调用进程阻塞,直到资源可用;若设置IPC_NOWAIT不会睡眠,进程直接返回EAGAIN错误/
/若val==0时阻塞等待信号量为0,调用进程进入睡眠状态,直到信号值为0;若设置IPC_NOWAIT,进程不会睡眠,直接返回EAGAIN错误/
short flag; /*0 设置信号量的默认操作*/
/IPC_NOWAIT设置信号量操作不等待/
/SEM_UNDO 选项会让内核记录一个与调用进程相关的UNDO记录,如果该进程崩溃,则根据这个进程的UNDO记录自动恢复相应信号量的计数值/
};
nsops:进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作
函数返回值
成功:返回信号量集的标识符
出错:-1,错误原因存于error中
错误代码
E2BIG:一次对信号量个数的操作超过了系统限制
EACCESS:权限不够
EAGAIN:使用了IPC_NOWAIT,但操作不能继续进行
EFAULT:sops指向的地址无效
EIDRM:信号量集已经删除
EINTR:当睡眠时接收到其他信号
EINVAL:信号量集不存在,或者semid无效
ENOMEM:使用了SEM_UNDO,但无足够的内存创建所需的数据结构
ERANGE:信号量值超出范围
sops为指向sembuf数组,定义所要进行的操作序列。下面是信号量操作举例。
struct sembuf sem_get={0,-1,IPC_NOWAIT}; /将信号量对象中序号为0的信号量减1/
struct sembuf sem_get={0,1,IPC_NOWAIT}; /将信号量对象中序号为0的信号量加1/
struct sembuf sem_get={0,0,0}; /进程被阻塞,直到对应的信号量值为0/
flag一般为0,若flag包含IPC_NOWAIT,则该操作为非阻塞操作。若flag包含SEM_UNDO,则当进程退出的时候会还原该进程的信号量操作,这个标志在某些情况下是很有用的,比如某进程做了P操作得到资源,但还没来得及做V操作时就异常退出了,此时,其他进程就只能都阻塞在P操作上,于是造成了死锁。若采取SEM_UNDO标志,就可以避免因为进程异常退出而造成的死锁。
- semctl函数原型
semctl (得到一个信号量集标识符或创建一个信号量集对象)
所需头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
函数说明
得到一个信号量集标识符或创建一个信号量集对象并返回信号量集标识符
函数原型
int semctl(int semid, int semnum, int cmd, union semun arg)
函数传入值
semid
信号量集标识符
semnum
信号量集数组上的下标,表示某一个信号量
cmd
见下文表15-4
arg
union semun {
short val; /SETVAL用的值/
struct semid_ds* buf; /IPC_STAT、IPC_SET用的semid_ds结构/
unsigned short* array; /SETALL、GETALL用的数组值/
struct seminfo *buf; /为控制IPC_INFO提供的缓存/
} arg;
函数返回值
成功:大于或等于0,具体说明请参照表15-4
出错:-1,错误原因存于error中
附加说明
semid_ds结构见上文信号量集内核结构定义
错误代码
EACCESS:权限不够
EFAULT:arg指向的地址无效
EIDRM:信号量集已经删除
EINVAL:信号量集不存在,或者semid无效
EPERM:进程有效用户没有cmd的权限
ERANGE:信号量值超出范围
表15-4 semctl函数cmd形参说明表
命令
解 释
IPC_STAT
从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
IPC_SET
设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
IPC_RMID
从内核中删除信号量集合
GETALL
从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中
GETNCNT
返回当前等待资源的进程个数
GETPID
返回最后一个执行系统调用semop()进程的PID
GETVAL
返回信号量集合内单个信号量的值
GETZCNT
返回当前等待100%资源利用的进程个数
SETALL
与GETALL正好相反
SETVAL
用联合体中val成员的值设置信号量集合中单个信号量的值