例:
a,b两进程,1个可用资源
a要申请使用资源,执行p操作,先减1,资源为0
这时b要申请使用资源,执行p操作,先减1,资源为-1,则阻塞
a使用结束,执行v操作,先加1,在没有加1时,资源为-1,表示有一进程在等待,加1后为0,唤醒一等待进程
b被唤醒,在没有执行加1时,资源为0,表示没有进程在等待。b使用资源后,执行v操作,加1后为1,结束。
进程之间的信号量使用
使用的头文件
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
取得已经存在的信号量或者创建一个新的信号量
key:所创建或打开信号量集的键值。和共享内存使用相同,一般也是要用ftok函数去产生
key为IPC_PRIVATE时,则创建一个信号量
key为其他值时,如果标识中包含IPC_CREAT,则表示要创建新的信号量
nsems:创建的信号量集中的信号量的个数,该参数只在创建信号量集时有效。
semflg:调用函数的操作类型,也可用于设置信号量集的访问权限,一般是semflg=0x644,两者通过or表示
semflg|IPC_CREAT 如果信号量已经存在,则返回信号量标识
semflg|IPC_CREAT|IPC_EXCL 如果信号量已经存在,要报错
返回值说明:
如果成功,则返回信号量集的IPC标识符。
如果失败,则返回-1,errno被设定成以下的某个值
EACCES:没有访问该信号量集的权限
EEXIST:信号量集已经存在,无法创建
EINVAL:参数nsems的值小于0或者大于该信号量集的限制;或者是该key关联的信号量集已存在,并且nsems
大于该信号量集的信号量数
ENOENT:信号量集不存在,同时没有使用IPC_CREAT
ENOMEM :没有足够的内存创建新的信号量集
ENOSPC:超出系统限制
int semop(int semid,struct sembuf *sops,size_t nsops);
操作一个或一组信号
semid:信号集的识别码
sops:指向存储信号操作结构的数组指针
注意:sops是一个sembuf的数组,即是一个信号量集,
struct sembuf
{
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
};
sem_num:操作信号在信号集中的编号,第一个信号的编号是0。
sem_op:如果其值为正数,该值会加到现有的信号内含值中。通常用于释放所控资源的使用权;
如果sem_op的值为负数,而其绝对值又大于信号的现值,操作将会阻塞,直到信号值
大于或等于sem_op的绝对值。通常用于获取资源的使用权;
如果sem_op的值为0,则操作将暂时阻塞,直到信号的值变为0。
使用时一般不为0
sem_flg:信号操作标志,可能的选择有两种
IPC_NOWAIT //对信号的操作不能满足时,semop()不会阻塞,并立即返回,同时设定错误信息。
SEM_UNDO //程序结束时(不论正常或不正常),保证信号值会被重设为semop()调用前的值。
这样做的目的在于避免程序在异常情况下结束时未将锁定的资源解锁,造成该资源永远锁定。
使用时一般都是SEM_UNDO
nsops:信号操作结构的数量,恒大于或等于1。
成功返回0,失败返回-1,errno被设为以下的某个值
E2BIG:一次对信号的操作数超出系统的限制
EACCES:调用进程没有权能执行请求的操作,并且不具有CAP_IPC_OWNER权能
EAGAIN:信号操作暂时不能满足,需要重试
EFAULT:sops或timeout指针指向的空间不可访问
EFBIG:sem_num指定的值无效
EIDRM:信号集已被移除
EINTR:系统调用阻塞时,被信号中断
EINVAL:参数无效
ENOMEM:内存不足
ERANGE:信号所允许的值越界
int semctl(int semid,int semnum,int cmd, /*union semun arg*/);
semid 信号量标识
semnum 操作信号在信号集中的编号,编号从0开始
cmd :
IPC_STAT读取一个信号量集的数据结构semid_ds,并将其存储在semun中的buf参数中。
IPC_SET设置信号量集的数据结构semid_ds中的元素ipc_perm,其值取自semun中的buf参数。
IPC_RMID将信号量集从内存中删除。
GETALL用于读取信号量集中的所有信号量的值。
GETNCNT返回正在等待资源的进程数目。
GETPID返回最后一个执行semop操作的进程的PID。
GETVAL返回信号量集中的一个单个的信号量的值。
GETZCNT返回这在等待完全空闲的资源的进程数目。
SETALL设置信号量集中的所有的信号量的值。
SETVAL设置信号量集中的一个单独的信号量的值。
第4个参数arg是一个semun的实例,
semun是在linux/sem.h中定义的:
union semun{
int val;/*value for SETVAL*/
struct semid_ds *buf;/*buffer for IPC_STAT&IPC_SET*/
ushort *array;/*array for GETALL&SETALL*/
struct seminfo *__buf;/*buffer for IPC_INFO*/
void *__pad;
};
val当执行SETVAL命令时使用。buf在IPC_STAT或者IPC_SET命令中使用。
代表了内核中使用的信号量的数据结构。array在使用GETALL或者SETALL命令时使用的指针
返回值:如果成功,则为一个正数。
如果失败,则为-1:errno=EACCESS(权限不够)
EFAULT(arg指向的地址无效)
EIDRM(信号量集已经删除)
EINVAL(信号量集不存在,或者semid无效)
EPERM(EUID没有cmd的权利)
ERANGE(信号量值超出范围)
注意系统调用semctl中的最后一个参数是一个联合类型的副本,而不是一个指向联合类型的指针。
需要注意的是,对于semun联合体,最好自己定义,否则GCC编译器可能会报“semun大小未知”。