前置服务器和后置服务器之间的通信 信号量加锁
使用IPC通信中的信号量 实现进程锁
1、为什么需要进程锁?
答:前置服务器和后置服务器两个进程之间,同时运行,一起争抢CPU的资源。
1.1CPU资源分配机制:时间片轮转。进程争抢CPU资源,但是抢到后可执行的时间没有规定(可能只执行了1秒,CPU资源又被别的进程抢走),同一个进程争抢CPU资源的次数也没有限制(可能抢到很多次,也可能很少)。
两个进程之间通信的方式是通过共享内存,共享内存是多个进程可以同时访问,没有权限限制。前置服务器接通共享内存后,后置服务器也可以同时接通共享内存。
由于这两个原因,出现资源争抢(①CPU执行权②共享内存读写权限)时,没有优先级规定,只有CPU自动分配的情况下,会造成数据紊乱,比如前置服务器还在写,后置服务器就将数据读走;后置服务器还在读数据,前置服务器又开始往里写。
简单例子:将CPU比作一个学校的厕所,到了下课时间,很多学生老师都想上厕所,但是厕所位置有限,无论是老师,还是学生,都是先到先得,老师也没有特权。当厕所位置被占满时,其他人只能排队等候。锁就相当于厕所的门,一个人上厕所时,关起门来,其他人就不能抢夺走他的位置。
2、进程锁 (使用信号量)是什么?
答:在ubuntu中可以通过指令ipcs查看 ,全称信号量数组
信号量是一种变量,它只能取正整数值,对这些正整数只能进行两种操作:等待和信号 用两种记号来表示信号量的这两种操作: P(semaphore variable) 代表等待 -1 V(semaphore variable) 代表信号 +1
2.2信号量分类
最简单的信号量是一个只能取“0”和“1”值的变量,也就是人们常说的“二进制信号量” 可以取多种正整数值的信号量叫做“通用信号量”
2.3PV操作的定义
假设我们有一个信号量变量sv,则pv操作的 定义如下
P(sv):如果sv的值大于零,就给它减去1;如果sv的值等于零,就挂起该进程的执行(因为信号量的值规定必须是正整数)
V(sv): 如果有其他进程因等待sv变量而被挂起,就让它恢复执行;如果没有进程因等待sv变量而被挂起,就给它加1,简单来说就是解锁操作,如果信号量因为执行过P操作,由1变成0,就做+1操作,使其恢复到1。
2.4 加锁后的流程图,
两个进程共享着sv信号量变量。如果其中之一执行了P(sv)操作,就等于它得到了信号量,也就能够进入关键代码部分了。 第二个进程将无法进入关键代码,因为当它尝试执行P(sv)操作的时候,它会被挂起等待一个进程离开关键代码并执行V(sv)操作释放这个信号量。
实现前置进程在共享内存中写数据时,后置进程只能等待。
3、如何实现?
三个函数semget() semctl() semop() 封装为sem_create()sem_val()sem_p()和sem_v()
/信号赋值联合体
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_create(key_t key,int nsems)
{
int res=semget(key,nsems,IPC_CREAT|0777);
if (res<0)
{
perror("semget error:");
}
return res;
}
//信号量是int型数组,给数组赋初值
int sem_val(int semid,int semnum,int value)
{
union semun arg;
arg.val = value;
int res=semctl(semid,semnum,SETVAL,arg);
if (res<0)
{
perror("semctl error");
}
return res;
}
//PV 操作,加锁,解锁操作
int sem_p(int semid,int semnum)
{
struct sembuf buf = {semnum,-1,0};//P操作 -1,0 设置信号量的默认操作
int res = semop(semid,&buf,1); //设置参数3等于1,只完成对一个信号量的操作
if (res<0)
{
perror("semop error");
}
return res;
}
//V操作 解锁
int sem_v(int semid,int semnum)
{
struct sembuf buf = {semnum,+1,0};
int res = semop(semid,&buf,1);
if (res<0)
{
perror("sem_v error");
}
return res;
}
使用方法:
int semid = sem_create((key_t)1003,1);//数组长度为1,表示一个信号量
//赋值操作
sem_val(semid,0,1);//第0个下标 赋值1
sem_p(semid, 0);//加锁
sem_p(semid, 0);//解锁
细节:前置进程需要赋值操作sem_val,将信号量赋值为1,后置进程不需要这一操作,如果后置进程二次初始化信号量,则等于没加锁。