linux进程间通信 -信号量

信号量

信号量本质上是一个计数器不设置全局变量是因为进程间是相互独立的,而这不一定能看到,看到也不能保证++引用计数为原子操作),用于多进程对共享数据对象的读取,它和管道有所不同,它不以传送数据为主要目的,它主要是用来保护共享资源(信号量也属于临界资源),使得资源在一个时刻只有一个进程独享

由于信号量只能进行两种操作等待和发送信号,即P(sem)和V(sem)他们的行为是这样的:

  • P(sem):如果sem的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
  • V(sem):如果有其他进程因等待sem而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.

二元信号量(引用计数为1)

二元信号量(Binary Semaphore)是最简单的一种锁(互斥锁),

它只用两种状态:

  • sem = 0 挂起。
  • sem = 1,执行

创建信号量集

API

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);

参数

key: 创建信号量的键值
nsems: 指定信号量集中需要的信号量数目,它的值几乎总是1
semflg:一组标志

  • IPC_CREAT : 标志后,即使给出的key是一个已有信号量的key,也不会产生错误。返回已有的信号量标识符
  • IPC_CREAT | IPC_EXCL : 可以创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。一般我们会还或上一个文件权限

返回值

成功: 信号量集的标识符
失败 : -1, 错误信息写入errno

错误代码

EACCES
密钥有一个信号集,但是调用进程没有访问该集的权限,并且在管理其IPC名称空间的用户名称空间中不具有CAP_IPC_OWNER功能。
EEXIST
在semflg中指定了IPC_CREAT和IPC_EXCL,但是密钥的信号量集已经存在。
EINVAL
nsems小于0或大于每个信号量集(SEMMSL)的信号量限制。
EINVAL
对应于key的信号量集已经存在,但是nsems大于该键集中的信号量。
ENOENT
没有为键设置信号量,并且semflg未指定IPC_CREAT。
ENOMEM
必须创建一个信号集,但是系统没有足够的内存来存储新的数据结构。
ENOSPC
必须创建一个信号集,但是将超出最大信号集集(SEMMNI)的系统限制或系统范围内最大信号量(SEMMNS)。

操作信号量

API

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
int semtimedop(int semid, struct sembuf *sops, size_t nsops,
const struct timespec *timeout);

参数

semid:信号量集标识符
sops: 指向进行操作的信号量集结构体数组的首地址
nsops: 进行操作信号量的个数,即sops结构变量的个数,需大于或等于1。最常见设置此值等于1,只完成对一个信号量的操作

sembuf 结构体:

unsigned short sem_num;  /* semaphore number */
           short          sem_op;   /* semaphore operation */
           short          sem_flg;  /* operation flags */

sem_num : 信号在信号集中的索引,0代表第一个信号,1代表第二个信号
sem_op:信号量在一次操作中需要改变的数据,通常是两个数,

  • sem_op > 0 信号加上 sem_op 的值,表示进程释放控制的资源;
  • sem_op = 0 如果没有设置 IPC_NOWAIT,则调用进程进入睡眠状态,直到信号量的值不为0;否则进程不回睡眠,直接返回 EAGAIN
  • sem_op < 0 信号加上 sem_op 的值。若没有设置 IPC_NOWAIT ,则调用进程阻塞,直到资源可用;否则进程直接返回EAGAIN

sem_flg: IPC_NOWAIT and SEM_UNDO,如果操作指定SEM_UNDO,它将会自动撤消该进程终止时

当 sembuf 的第二个数据结构 sem_op 设置为负数时,是对它进行P操作,即减1操作;当设置为正数时,就是进行V操作,即加1操作。

返回值

成功: 信号量集的标识符
出错: -1,错误原因存于error中

错误代码

E2BIG:一次对信号量个数的操作超过了系统限制
EACCESS:权限不够
EAGAIN:使用了IPC_NOWAIT,但操作不能继续进行
EFAULT:sops指向的地址无效
EIDRM:信号量集已经删除
EINTR:当睡眠时接收到其他信号
EINVAL:信号量集不存在,或者semid无效
ENOMEM:使用了SEM_UNDO,但无足够的内存创建所需的数据结构
ERANGE:信号量值超出范围

删除和初始化信号量

API

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, … )

参数

  • sem_id : semget返回的信号量标识符
  • semnum : 信号量集的哪一个信号量
  • cmd:
cmd说明
IPC_STAT从信号量集上检索semid_ds结构,并存到semun联合体参数的成员buf的地址中
IPC_SET设置一个信号量集合的semid_ds结构中ipc_perm域的值,并从semun的buf中取出值
IPC_RMID从内核中删除信号量集合
GETALL从信号量集合中获得所有信号量的值,并把其整数值存到semun联合体成员的一个指针数组中 (array)
GETNCNT返回当前等待资源的进程个数
GETPID返回最后一个执行系统调用semop()进程的PID
GETVAL返回信号量集合内单个信号量的值(val)
GETZCNT返回当前等待100%资源利用的进程个数
SETALL与GETALL正好相反 (array)
SETVAL用联合体中val成员的值设置信号量集合中单个信号量的值,(val)
  • 第4个参数
    如果cmd为SETVAL,那么该参数为val
    如果cmd为IPC_STAT&IPC_SET,那么该参数为struct semid_ds结构体变量
    如果cmd为GETVAL&SETALL,则该参数为数组地址array。
    如果cmd为IPC_INFO,则该参数为strcut seminfo结构体变量__buf

如有需要第四个参数一般设置为union semnu arg;定义如下

 union semun {
               int              val;    /* Value for SETVAL */
               struct semid_ds *buf;    /* Buffer for IPC_STAT, IPC_SET */
               unsigned short  *array;  /* Array for GETALL, SETALL */
               struct seminfo  *__buf;  /* Buffer for IPC_INFO  一般不需要
                                           (Linux-specific) */
           };

semid_ds 结构体

       struct semid_ds {
           struct ipc_perm sem_perm;  /* Ownership and permissions */
           time_t          sem_otime; /* Last semop time */
           time_t          sem_ctime; /* Last change time */
           unsigned long   sem_nsems; /* No. of semaphores in set */
       };

返回值

GETVAL //返回的是semnum的值
GETALL //设置semnum为0,那么获取信号集合的地址传递给第四个参数。返回值为0,-1
GETNCNT //设置semnum为0,返回值为等待信号量值的递增进程数,否则返回-1
GETZCNT //设置semnum为0,返回值是等待信号量值的递减进程数,否则返回-1
SETVAL //将第四个参数指定的值设置给编号为semnum的信号量。返回值为0,1
SETALL //设置semnum为0,将第四个参数传递个所有信号量。返回值0,1

错误代码

EACCESS:权限不够
EFAULT:arg指向的地址无效
EIDRM:信号量集已经删除
EINVAL:信号量集不存在,或者semid无效
EPERM:进程有效用户没有cmd的权限
ERANGE:信号量值超出范围

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值