👉一、前言
👉二、认识信号量
1、信号量的概念
- 信号量是一种变量,它只能取正整数值,对这些正整数只能进行两种操作:等待和信号
- 最简单的信号量是一个只能取“0”和“1”值的变量,也就是人们常说的“二进制信号量”
- 假设进程一和进程二共享同一个信号量,有如下两个场景
场景一
初始sv=1,进程一先访问信号量,进行P操作后sv=0,此时进程二也访问了信号量并想要执行P操作,但是由于此时sv=0,所以进程二只能先挂起,直至进程一执行完业务,进行V操作后,即sv+1=1后,进程二恢复执行。
场景二
进程一业务做完,执行V操作,sv+1=1,进程二恢复执行P操作,sv-1=0。
2、信号量的作用
- 信号量可以解决进程同步问题
下面我们来学习一下信号量的相关函数
👉三、信号量相关函数
1、semget()函数
- 函数功能:创建一个新的信号量或者取得一个现有信号量的键字
- 代码示例
int sem_create(key_t key, int nsems)
{
int res = semget(key, nsems, IPC_CREAT | 0777);
if (res < 0)
{
perror("semget error");
}
return res;
}
2、semctl()函数
- 函数功能:允许我们直接控制信号量的信息
- 补充
- union semun复合结构如下
- 代码示例
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) */
};
//信号量设置值
int sem_setval(int semid, int senindex, int val)
{
//senindex:信号量的编号,一般取值为 0
union semun arg;
arg.val = val;//设置具体值
int res = semctl(semid, senindex, SETVAL, arg);
if (res < 0)
{
perror("semctl error");
}
return res;
}
3、semop()函数
- 函数功能:改变信号量的键值
- struct sembuf 结构体如下
- 代码示例
//信号量p操作 -1
int sem_p(int semid, int semindex)
{
//senindex:信号量的编号,一般取值为 0
struct sembuf buf = { semindex, -1, SEM_UNDO };
int res = semop(semid, &buf, 1);
if (res < 0)
{
perror("semop error");
}
return res;
}
//信号量v操作 +1
int sem_v(int semid, int semindex)
{
struct sembuf buf = { semindex, 1, SEM_UNDO };
int res = semop(semid, &buf, 1);
if (res < 0)
{
perror("semop error");
}
return res;
}
👉四、主函数测试
- 创建两个工程,访问同一个信号量,并执行P、V操作。
- 调用的函数为上面示例代码所封装的
1、工程一
- 工程一为创建信号量,并赋初值为1
int main()
{
//如果1001信号量存在则访问 不存在则创建
int semid = sem_create((key_t)1001, 1);
//将信号量数组下标为0的数据设置为1
sem_setval(semid, 0, 1);
//加锁 信号量数组下标为0 执行P操作后信号量的值为0
sem_p(semid, 0);
for (int i = 0; i < 5; i++)
{
cout << "第一个进程正在运行…… i = " << i << endl;
sleep(1);
}
//解锁,执行V操作后 信号量的值为1
sem_v(semid, 0);
return 0;
}
2、工程二
- 工程二为访问上一个工程创建的信号量,不需要再赋初值。
int main()
{
//如果1001信号量存在则访问 不存在则创建
int semid = sem_create((key_t)1001, 1);
//加锁 -1
sem_p(semid, 0);//阻塞无法继续执行 只有上一个工程结束后才执行
for (int i = 0; i < 5; i++)
{
cout << "第二个进程正在运行…… i = " << i << endl;
sleep(1);
}
//解锁 +1
sem_v(semid, 0);
return 0;
}
3、先执行工程一立马执行工程二
- 测试效果如下
🔔Tip
😘The end ……🔚