一.首先我们必须知道什么是信号量
信号量的本质是数据操作锁,它本身不具备数据交换功能,而只是用于保护进程线程之间共享的资源,实现对共享资源的同步与互斥操作。
信号量相当于一把锁,例如小黑上厕所,需要检查是否有锁,有锁获取锁,占用厕所坑位资源,其他人无法进此坑位,当小黑上完厕所,需要释放锁,其他人都可以获取锁。
小黑 ------》 进程
门锁 --------》 信号量
坑位 ------》 共享资源(信号量保护对象)
二.为什么要使用信号量
为了防止出现因多个程序同时访问一个共享资源而引发的一系列问题,我们需要一种方法,在任一时刻只能有一个执行进程访问代码的临界区域;临界区域是指执行数据更新的代码需独占式地执行。而信号量就可以提供这样的一种访问机制,让一个临界区同一时间只有一个进程在访问它,也就是说信号量是用来调协进程对共享资源的访问的。其中共享内存的使用就要用到信号量。
临界资源:一次只允许一个进程(一个线程)使用的资源叫做临界资源。
临界区:访问临界资源的代码称为临界区。
三.信号量分类:
1.System V信号灯(IPC对象),也叫经典ipc对象,一般用于进程通信。
2.posix基于内存的信号灯(无名信号灯),一般线程通信。
功能分类:
1.二值信号量:信号量的值为0或1。与互斥锁类似,资源可用时值为1,不可用时值为0。
2.计数信号量:值在0到n之间。用来统计资源,其值代表可用资源数。
注意:system v信号量对象不是一个信号量,是一个或者多个信号量的集合。对应内核中一个结构体:struct semid_ds 有一个成员sem_base指向第0个信号量结构体起始地址。
四.System v信号量实现步骤
-----》1、创建信号量对象
int semget(key_t key, int nsems, int semflg);
Key:创建信号量对象的唯一键值
nsems表示的就是创建的信号量集中信号量的个数
Semflg: 权限 IPC_CREAT:存在则打开,否则创建; IPC_CREAT | IPC_EXCL存在则出错返回,否则创建,这 样保证了 打开的是一个全新的信号量集
成功:信号量集ID 失败-1
----》2.初始化具体信号量的值。
int semctl(int semid, int semnum, int cmd, union set);
semid:信号灯集ID
semnum: 要修改的信号灯编号
Cmd: GETVAL:获取信号灯的值
SETVAL:设置信号灯的值
IPC_RMID:从系统中删除信号灯集合
-----》3.执行p操作:获取资源,信号量值减1不阻塞,可以操作资源;如果无资源可用,信号量值为0,阻塞等待资源。
int semop(int semid, struct sembuf *sops, unsigned nsops);
semid:信号量集ID
struct sembuf {
short sem_num; // 要操作的信号灯的编号
short sem_op; // 0 : 等待,直到信号灯的值变成0
// 1 : 释放资源,V操作
// -1 : 分配资源,P操作
short sem_flg; // 0, IPC_NOWAIT, SEM_UNDO
};
nops: 要操作的信号灯的个数
----》 操作资源: 共享内存
-----》4.执行v操作:释放资源,信号量值加1.
int semop(int semid, struct sembuf *sops, unsigned nsops);
-----》5.删除对象
int semctl(int semid, int semnum, int cmd, ...);