信号量集知识整理

信号量是最早出现的用来解决 进程同步与互斥问题的机制,包括一个称为信号量的 变量及对它进行的两个 原语操作。 信号量为一个整数,我们设这个信号量为:sem。很显然, 我们规定在sem大于等于零的时候代表可供并发进程使用的资源实体数sem小于零的时候,表示正在等待使用 临界区 的进程的个数。根据这个原则,在给 信号量附初值的时候,我们显然就要 设初值大于零
p操作和v操作是不可中断的 程序段,称为原语。P,V原语中P是 荷兰语的Passeren,相当于英文的pass, V是荷兰语的Verhoog,相当于英文中的increment。
P 原语操作的动作是:
(1) sem减1; // 首先要减1
(2) 若sem 减1后仍大于或等于零,则进程继续执行;// 注意 sem表示可用资源数,如果有可用资源,则不可能有进程在等待
(3) 若sem减1后小于零,则该进程被阻塞后进入与该信号相对应的 队列中,然后转 进程调度。 // sem小于零的时候,表示正在等待使用 临界区 的进程的个数
V 原语操作的动作是:
(1) sem加1; // 首先要加1,在没有加1时如果是小于0,则表示在获得v原语操作的进程的后面还有进程在等待,所以加1后要去唤醒一等待进程
(2) 若相加结果大于零,则进程继续执行; /
(3) 若相加结果小于或等于零,则从该信号的 等待队列中唤醒一等待进程,然后再返回原进程继续执行或转 进程调度
需要提醒大家一点就是P,V操作对于每一个进程来说,都只能进行一次。而且必须成对使用。且在P,V 原语执行期间不允许有中断的发生。

例:

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大小未知”。

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值