信号量
信号量:解决进程之间通信时,资源的一个同步和互斥。
对信号量由两个操作(PV操作),这两个操作都是原子的。
原子操作:要不执行,要不执行,一旦执行肯定可以完成。
信号量对于某种资源(buffer),是一个非负的整型值。信号量>0时,就表示资源可以用,
你就可以进行原子操作。信号量==0,这里就不能操作这个资源,就会阻塞在这句代码处。
P操作:(>0),就是操作资源,占用一个资源后,就对这个信号量(-1),操作完成后,必须信号量(+1)。
V操作:(==0)没有进程排队等待,释放一个资源(+1);如果有这个信号量, 激活某一个进程。
信号量的工作流程:
S代表信号量;R代表资源
1、init(S),初始化信号量(创建一个信号量: 1(一般做法))
非临界区:
P(S)
临界区:资源R,允许有限个进程操作该资源(1个)
V(S)
非临界区
简单的信号量:0和1;这个信号量叫做二维信号量
说一下:linux二维信号量的应用步骤:
1、创建一个信号量或者获取已经在系统中存在的信号量,获取信号量semget,
不同的进程可以通过同一键值获得同一个信号量。
2、初始化信号量,函数semctl。
3、对信号量进行PV操作,函数semop.
4、删除系统中的信号量,函数semctl。PS:不要重复删除(已删除的)信号量。
介绍以下:
1、int semget(key_t key, int nsems, int semflg);
key:键值,用法,参考之前。
nsems:创建信号量的数目,通常写1。
semflag:参考open,注意:可以或上权限的。
2、int semctl(int semid, int semnum, int cmd, …);
semid:第1步获得的信号量标识符数字。
semnum:用到信号量(集合)时,才会使用。单个信号就写0。
cmd:
IPC_RMID表示:删除系统信号量。成功返回0,失败-1.
IPC_STAT表示:获得信号量(信号量集合)的 struct semid_ds,存在第4个参数所指向的buf中,
这个结构体就是描述系统信号量的数据结构。成功返回0,失败-1.
SETVAL表示:信号量的值(初始值)在第四个参数arg的内部元素(val)设置。成功返回0,失败-1.
第四个参数:union semun arg,有时候可能系统不认识这个共用体,你就得自己定义。
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) */
};
3、int semop(int semid, struct sembuf *sops, unsigned nsops);
第二个参数的结构体类型:
//信号量编号,使用单个信号量时,值写为0
unsigned short sem_num; /* semaphore number */
// 写为1,就表示V操作,写-1,就表示P操作。
short sem_op; /* semaphore operation */
// SEM_UNDO 表示系统退出后,如果有未被释放的信号量,就自动把这个信号量释放。
short sem_flg; /* operation flags */
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <stdlib.h>
union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
//初始化信号量
int init_sem(int semid, int init_val)
{
union semun arg;
//信号量的初始值
arg.val = init_val;
if (semctl(semid, 0, SETVAL, arg) == -1)
{
perror("semctl初始化");
exit(-1);
}
return 0;
}
//信号量P操作
int sem_p(int semid)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = -1;
sem_b.sem_flg = SEM_UNDO;
//最后一个参数写成1,表示只操作一个结构体
if (semop(semid, &sem_b, 1) == -1)
{
perror("P操作");
exit(-1);
}
return 0;
}
//信号量V操作
int sem_v(int semid)
{
struct sembuf sem_b;
sem_b.sem_num = 0;
sem_b.sem_op = 1;
sem_b.sem_flg = SEM_UNDO;
//最后一个参数写成1,表示只操作一个结构体
if (semop(semid, &sem_b, 1) == -1)
{
perror("V操作");
exit(-1);
}
return 0;
}
//删除信号量
int del_sem(int semid)
{
union semun arg;
if (semctl(semid, 0, IPC_RMID, arg) == -1)
{
perror("semctl删除");
exit(-1);
}
return 0;
}
/*
控制父子进程的执行顺序
*/
#define DELAY 3
int main(void)
{
pid_t pid;
int semid = 0;
//创建一个信号量
semid = semget(ftok(".", 'A'), 1, 0666 | IPC_CREAT);
if (semid < 0)
{
perror("semget");
exit(0);
}
//初始化信号量
init_sem(semid, 0);
//创建进程
pid = fork();
if (pid < 0)
{
perror("fork");
exit(0);
}
if (pid == 0)
{
//子进程
printf("子进程开始......\n");
sleep(DELAY);
printf("子进程的pid = %d.\n", getpid());
//释放一个资源
sem_v(semid);
}
if (pid > 0)
{
// 父进程
sem_p(semid);
printf("父进程的pid = %d.\n", getpid());
sem_v(semid);
//删除信号量
del_sem(semid);
}
return 0;
}