信号量是什么
资源的竞争
资源竞争 : 当多个进程同时访问共享资源时,会产生资源竞争,最终最导致数据混乱
临界资源 : 不允许同时有多个进程访问的资源,包括硬件资源(CPU、内存、存储器以及其他外围设备)与软件资源(共享代码段、共享数据结构)
临界区 : 访问临界资源代码
可以理解为,狼多肉少,假如有5个人要喝水,但只有一个杯子,为了不让他们打起来,你设定了规则,指到谁,谁才能喝水
信号量: 由内核维护的整数,其值被限制为大于或等于0
信号量可以执行如下操作:
将信号量设置成一个具体的值
在信号量当前值的基础上加上一个数值
在信号量当前值的基础上减上一个数值
等待信号量的值为 0;
一般信号量分为 二值信号量 与 计数信号量
二值信号量:一般指的是信号量的值为1,可以理解为只对应一个资源
计数信号量:一般指的是值大于等于2 ,可以理解为对应多个资源
在 Linux 系统中查询信号量使用 ipcs -s
可以理解为二维数组
创建信号量集合
创建信号量集合调用 semget 函数
函数原型
int semget(key_t key, int nsems, int semflg);
key:由ftok()函数生成
nsems:信号量的数量
semflg:信号量集合的标志
IPC_CREAT:创建标志
IPC_EXCL:与IPC_CREAT标志一起使用,如果信号量集合存在就报错
权限标志
函数返回值
成功:返回信号量集合的id
失败:-1,并设置errno
创建后,需要初始化信号量集合
初始化信号量调用 semctl 函数
int semctl(int semid, int semnum, int cmd, …);
semid:信号量集合的id
semnum:信号量的编号,信号量的编号从0开始
cmd:命令控制字
注意:使用命令时需要使用 union semun 共用体,可在man 手册查看
union semun sem;
key_t key = ftok(SEM_PATHNAME,SEM_PROJ_ID);
if(key == -1)
{
perror("ftok");
exit(EXIT_FAILURE);
}
int semid = semget(key,1,IPC_CREAT|0644);
if(semid == -1)
{
perror("semget");
exit(EXIT_FAILURE);
}
printf("semid = %d\n",semid);
sem.val = 1;
int ret = semctl(semid,0,SETVAL,sem);
if(ret == -1)
{
perror("semctl");
exit(EXIT_FAILURE);
}
信号量操作
信号量可以进行以下操作:
对信号量的值加1
对信号量的值减1
等待信号量的值为0
操作信号量调用 semop 函数
int semop(int semid, struct sembuf *sops, size_t nsops);
semid:信号量集合id
sops:信号量操作结构体指针
nsops:操作的信号量的数量
struct sembuf 结构体
sements of this structure are of type struct sembuf, containing the following
members:
unsigned short sem_num; /* semaphore number */
short sem_op; /* semaphore operation */
short sem_flg; /* operation flags */
删除信号量集合
信号量集合调用 semctl 函数,设置命令为 IPC_RMID
int semctl(int semid, int semnum, int cmd, ...);
一般删除 semctl(semid,0,IPC_RMID,NULL);
信号量互斥
通过创建一个信号量集合,包含 2个信号量,一个信号量编号为 0 (SEM_CONTROL_P)控制父进
程的运行与暂停,一个信号量编号为1(SEM_CONTROL_C) 控制子进程的运行与暂停
信号初始化
SEM_CONTROL_P : 初始化为 0
SEM_CONTROL_C : 初始化为 1
子进程
占用 SEM_CONTROL_C ,此时子进程阻塞
当父进程释放 SEM_CONTROL_C 时, 子进程输出 B ,释放 SEM_CONTROL_P
循环占用 SEM_CONTROL_C,由于之前已经占用,此时进入子进程阻塞,等待父进程释放
SEM_CONTROL_C
父进程
占用 SEM_CONTROL_P,此时父进程正常运行,输出 A
释放 SEM_CONTROL_C,占用 SME_CONTROL_P,此时父进程阻塞,子进程继续执行
当子进程输出 B 之后,释放 SEM_CONTROL_P,父进程继续执行,输出 A
父进程 释放 SEM_CONTROL_P 循环结束
可以理解为,两个信号量的集合,一个值为一,一个为0(集合默认从0开始编号)