SystemV信号量主要用于解决生产者和消费者问题,一个信号量能够控制多个资源,说它是信号量集也不为过。
#include
#include
#include
/**
* @brief 创建信号量或者返回已存在的信号量
*
* @params key 与信号量关联的key,三种生成方式,包括IPC_PRIVATE,注意key和信号量不是绑定关系,相同的key产生的标识符不一定相同,比如系统重启的情况
* @params nsems 集合中信号量的个数
* @params shmflg 标志位和权限控制标志位,可以多个用or运算。IPC_CREAT、 IPC_EXCL
*
* @returns 成功返回信号量集标识符,失败返回-1
*/
int semget(key_t key, int nsems, int semflg);
#include
#include
#include
/**
* @brief 操作信号量集
*
* @params semid 信号量集标识符
* @params sops 具体的操作行为,指向一个数组,每个元素都是sembuf结构,该结构指定了要操作哪个信号量以及相应的值等。
*
* @params nsops 表名sembuf的数量
*
* @returns 成功返回0,失败返回-1,注意这里的操作是原子性的,如果操作多个信号量,要么全部成功,要么什么都不做
*/
int semop(int semid, struct sembuf *sops, size_t nsops);
struct sembuf {
unsigned short int sem_num; // 指定操作信号量的下标,从0开始
short sem_op; // 信号量的值
short sem_flg; // 操作标志位,可选 IPC_NOWAIT and SEM_UNDO,前者表示非阻塞,后者标志当进程退出后,系统自动恢复对这个信号量的操作。
};
#include
#include
#include
/**
* @brief 操作信号量集
*
* @params semid 信号量集标识符
* @params semnum 要操作的信号量序号,下标从0开始
* @params cmd 控制行为,可选值IPC_STAT,IPC_RMID, IPC_SET, IPC_INFO
* @params 可选参数,这里的参数结构体需要用户自定义。
* @returns 不同的cmd返回不通值
*/
int semctl(int semid, int semnum, int cmd, ...);
// 可选参数,需要用户自定义
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) */
};
下面是信号量集的一个简单示例,分为生产者和消费者,其中生产者生产资源,消费者消费资源
生产者:
/*
** Name: sem_wait.c
** Desc: 生产者
** Author: masonf
** Date: 20200626
*/
#include
#include
#include
#include
#include
// 自定义结构体
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int main()
{
int ret = 0;
key_t key = 10240;
int sem_id;
// 创建或获取信号量集,其中包含一个信号量,这里key是固定的
sem_id = semget(key, 1, IPC_CREAT);
if (sem_id == -1)
{
perror("create sem error");
return -1;
}
// 设置第0个信号量,资源数加1
struct sembuf sem_operation[1]={0};
sem_operation[0].sem_op=1;
sem_operation[0].sem_num = 0;
// 初始化信号量的值为1
if (semop(sem_id, sem_operation, 1) == -1)
{
perror("init sem error");
// 初始化失败则删除信号量集
semctl(sem_id, 1, IPC_RMID);
return -1;
}
int food_nums = 1;
while (food_nums < 5)
{
printf("start producing foods\n");
// 生产食物
sem_operation[0].sem_num=0; // 操作第一个信号量,下标从0开始
sem_operation[0].sem_op = 1;
while (semop(sem_id, sem_operation, 1) == -1)
{
printf("producing error, continue\n");
continue;
}
++food_nums;
}
// 删除信号量集
//semctl(sem_id, 1, IPC_RMID);
printf("work done, Go home!\n");
return 0;
}
消费者
/*
** Name: sen_wait.c
** Desc: 消费者
** Author: mason
** Date: 20200626
*/
#include
#include
#include
#include
// 自定义结构体
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
int main()
{
int ret = 0;
key_t key = 10240;
int sem_id;
// 创建或获取信号量集
sem_id = semget(key, 1, IPC_CREAT);
if (sem_id == -1)
{
perror("create sem error");
return -1;
}
//操作信号量,默认是阻塞
struct sembuf sem_operation[1]={0};
sem_operation[0].sem_num=0;
sem_operation[0].sem_op=-1; // 申请一个资源
//sem_operation[0].sem_flg=SEM_UNDO;
// 消费,每次消费一个
while (semop(sem_id, sem_operation, 1) != -1)
{
printf("consuming foods\n");
}
return 0;
}
编译运行
4.1 信号量的创建和初始化操作是分开的,这点需要注意,好在有一种解决方法,信号量集相关的结构体中有一个sem_otime字段,初始化为0,可以通过检查sem_otime是否为0来判断是否已经经过初始化
2. 资源限制
SEMMSL 系统范围内信号量集的最大数量,可通过/proc/sys/kernel/sem查看
SEMMSL 单个信号量集中信号量的最大个数
SEMMNS 系统范围内信号量的最大数量
1. https://man7.org/linux/man-pages/man2/semop.2.html
2. 《Linux环境编程 从应用到内核》
3. 《Unix网络编程 卷二 进程间通信》
================================================================================================
Linux应用程序、内核、驱动、后台开发交流讨论群(745510310),感兴趣的同学可以加群讨论、交流、资料查找等,前进的道路上,你不是一个人奥^_^。...