信号量
概念
特点
- 信号量实际是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储 进程间通信 数据。很多进程会访问同一资源,或者向共享内存写入一些东西,为防止争夺资源混乱。可以给一些进程上锁,让其排队等待
工作原理
- P(sv):如果sv的值大于零,就给它减1;如果它的值为零,就挂起该进程的执行
- V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运行,如果没有进程因等待sv而挂起,就给它加1.
在信号量进行PV操作时都为原子操作(因为它需要保护临界资源)
注:原子操作:单指令的操作称为原子的,单条指令的执行是不会被打断的
步骤:
- 创建或者使用信号灯集
- 对某信号灯做PV操作(P减法,V加法)
- 删除信号灯集(非必须)
创建/获取信号量集
semphore get
功能
- 创建一个新信号量或获取一个已有信号量
函数原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
key
:唯一非零的整数值(和共享内存中的参数定义一致,key
不变可以获取相同的信号灯集)IPC_PRIVATE
ftok()
nsems
:创建的信号量的个数(PV操作的钥匙个数)semflg
:一组标志IPC_CREAT | 0666
:当信号量不存在时创建一个新的信号量,并指定权限IPC_CREAT | IPC_EXCL | 0666
:创建一个新的,唯一的信号量,如果信号量已存在,返回一个错误。
返回值
- 成功返回一个相应信号量集的标识号(非负整数)
- 失败返回-1,并设置 errno
注意事项
-
nsems
是当前信号集中的信号量的个数,不是value
值,semid
是用于标识这一组信号量(即标识信号量集) -
示例:
semget(key, 2, IPC_CREAT | 0666);
- 创建一个信号量集,里面包含两个信号量,一个下标为0,另一个下标为1
- 初始的value值都为0(没有通过
semctl()
修改),所以一旦先执行P操作,就会阻塞。
semget error: Invalid argument
-
原因:semget之前创建的信号量集未删除(即
nsms
不一致) -
解决:使用命令
ipcrm -s semid
(semid为具体的信号量集标识符)删除系统中的信号量集,重新创建
使用命令查看信号量
ipcs -s # # interprocess communication status——进程间通信的状态 s——semaphore
PV操作
功能
- 对某些信号量做 P(减法)和 V(加法)操作
函数原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
-
semid
:信号量集标识号,semget()
的返回值 -
sops
:一个指向struct sembuf
类型的指针struct sembuf { unsigned short int sem_num; // 信号量组中对应的序号,0~sem_nums-1 short int sem_op; // 信号量值在一次操作中的改变量 +5(V操作) -5(P操作) short int sem_flg; /* operation flag */ };
sem_flg
:0 阻塞 IPC_NOWAIT 不阻塞
-
nsops
:操作的信号灯个数
返回值
- 成功返回0
- 失败返回-1,并设置 errno
示例1
struct sembuf ss1 = {0, -1, 0};
struct sembuf ss1 = {-1, -2, 0};
struct sembuf arr[2] = {ss1, ss2};
semop(semid, arr, 2); // semid为具体的信号灯标识号
示例2
struct sembuf ss = {0, -5, 0};
semop(semid, &ss, 1);
删除信号量集
功能
- 对信号量集进行控制
函数原型
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
-
semid
:信号量集的标识号 -
semnum
:信号量集中的某个信号量的下标 -
cmd
-
SETVAL
:设置某个信号量的 value 值(这个值通过union semun
中的val成员设置)-
union semum
结构体union semun { int val; /* 用于 SETVAL 的值 */ struct semid_ds *buf; /* 用于 IPC_STAT、IPC_SET 的缓冲区 */ unsigned short *array; /* 用于 GETALL、SETALL 的数组 */ struct seminfo *__buf; /* 用于 IPC_INFO(Linux 特定)的缓冲区 */ };
-
示例:
union semun us; us.val = 5; semctl(semid, 0, SETVAL, us, NULL); // 并没有要求必须以NULL结尾
-
-
GETVAL
:获取到 某个信号量的 value 值-
示例:
semctl(semid, 0, GETVAL, NULL);
-
-
IPC_RMID
:删除整个信号量集,不需要缺省参数,只需要三个参数即可-
示例:
semctl(semid, 0, IPC_RMID, NULL);
-
-
返回值
- 成功,根据
cmd
返回一个非负值- 对于
SETVAL
、GETVAL
、IPC_RMID
返回0代表成功
- 对于
- 失败,返回-1,并设置errno
linux union semun报错storage size isn‘t known
-
原因:内核中的union sem_union联合体被注释
-
解决:自己实现
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED) /* union semun is defined by including <sys/sem.h> */ #else /* according to X/OPEN we have to define it ourselves */ union semun { int val; /* value for SETVAL */ struct semid_ds* buf; /* buffer for IPC_STAT, IPC_SET */ unsigned short* array; /* array for GETALL, SETALL */ /* Linux specific part: */ struct seminfo* __buf; /* buffer for IPC_INFO */ }; #endif
使用命令删除信号量集
ipcrm -s # interprocess communication remove s——semphore
综合示例
semget_p
概述:
- 通过调用
ftok
函数,创建唯一键值 - 创建/返回信号量集(初始时value为0)
- 修改value值为5
- 执行p操作,需要value值为6,不够,故阻塞
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun
{
int val; /* value for SETVAL */
struct semid_ds* buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short* array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo* __buf; /* buffer for IPC_INFO */
};
#endif
int main(int argc, char* argv[])
{
key_t key;
int semid;
struct sembuf* sops;
int ret;
// 创建key
if (-1 == (key = ftok("../ftok.txt", 7)))
{
perror("ftok error");
exit(-1);
}
printf("key:%#x\n", key);
// 创建信号量集,当前集合中有3个信号量,下标分别为012,初始value为0
if (-1 == (semid = semget(key, 3, IPC_CREAT | 0666)))
{
perror("semget error");
exit(-1);
}
printf("semid:%d\n", semid);
// V操作, 下标0,增加1个value值,0表示阻塞
struct sembuf ss = {0, 1, 0};
if (-1 == (ret = semop(semid, &ss, 1)))
{
perror("semop error");
exit(-1);
}
printf("I am back--v\n");
// 删除整个信号灯集
semctl(semid, 0, IPC_RMID, NULL);
return 0;
}
semget_v
概述:
- 通过调用
ftok
函数,创建唯一键值 - 创建/返回信号量集(初始时value为0)
- 修改value值为5(二次修改,会立即生效,即使此时P操作还在阻塞)
- 如果直接修改value值为6,此时P操作(需要value值为5)会立刻结束阻塞
- 执行V操作,释放一个value值,P操作结束阻塞
#include <stdio.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#if defined(__GNU_LIBRARY__) && !defined(_SEM_SEMUN_UNDEFINED)
/* union semun is defined by including <sys/sem.h> */
#else
/* according to X/OPEN we have to define it ourselves */
union semun
{
int val; /* value for SETVAL */
struct semid_ds* buf; /* buffer for IPC_STAT, IPC_SET */
unsigned short* array; /* array for GETALL, SETALL */
/* Linux specific part: */
struct seminfo* __buf; /* buffer for IPC_INFO */
};
#endif
int main(int argc, char* argv[])
{
key_t key;
int semid;
struct sembuf* sops;
int ret;
union semun us;
// 创建key
if (-1 == (key = ftok("../ftok.txt", 7)))
{
perror("ftok error");
exit(-1);
}
printf("key:%#x\n", key);
// 创建信号量集,当前集合中有3个信号量,下标分别为012,初始value为0
if (-1 == (semid = semget(key, 3, IPC_CREAT | 0666)))
{
perror("semget error");
exit(-1);
}
printf("semid:%d\n", semid);
// 修改信号量的value值(修改后,会立即剩下,即使p操作还在阻塞状态)
us.val = 5;
// 并没有要求必须以NULL结尾
if (0 != semctl(semid, 0, SETVAL, us, NULL))
{
perror("semctl error");
exit(-1);
}
// V操作, 下标0,增加1个value值,0表示阻塞
struct sembuf ss = {0, 1, 0};
if (-1 == (ret = semop(semid, &ss, 1)))
{
perror("semop error");
exit(-1);
}
printf("I am back--v\n");
// 删除整个信号灯集
semctl(semid, 0, IPC_RMID, NULL);
return 0;
}
标签:信号量,struct,IPC,int,semid,就够,间通信,value
From: https://www.cnblogs.com/General-xd/p/18393653