在多进程的编程环境,进程往往需要共享某些资源,比如共享内存,文件。在多个进程同时对这些共享资源操作时候,可能会引起冲突。为了更好的控制对资源的访问,linux提供了一种成为信号量的机制,首先要认识到PV操作原理。这个是一个荷兰数学家解决一个项目的时候提出来的,为了解决多进程,单处理器条件下进程资源的分配问题。据说灵感来源于火车运行控制里面的信号灯。(semphore)
举个例子什么情况下会出错,假设进程A和进程B都对共享内存内存进行了映射,进程A负责向共享内存写入数据,进程B负责向共享内存读取数据,如果不对读写进程进行控制,可能会出现进程A正在向共享内存写入数据但是尚未写完,此时进程B正好也开始从共享内存读数据,B读到的数据显然不是预期的。
先来看下什么是PV操作,P在荷兰语是“缩写”的第一个字母,V是“释放”的第一个字母,P和V操作都是不可中断的过程,由操作系统保证,计算机内核在接收到中断后会进行中断函数处理,完成后返回原来的位置,一旦执行了P或则V操作任何信号都不能中断,就是忽视中断。
PV操作过程是前提假定存在整形变量sem,PV操作就是对这个数进行加减,没什么难的。
P操作:sem减1,若sem减去1后大于等于0,P操作返回,该进程继续执行。若小于0该进程被阻塞,进入操作系统的阻塞序列。
V操作:sem加1,若结果大于0,P操作返回该进程继续执行,若相加的结果小于或者等于0,从操作系统的阻塞进程的序列唤醒一个在该信号上的进程,然后在返回原进程继续。
Linux系统下的信号量机制在标准的PV操作基础上进行了扩充,主要体现在一下几个方面:
1.支持信号量组,一次可以创建一组信号量,并可以对组内的信号量进行不同操作。
2.每次信号量的PV操作不仅限于对信号量值加1或者减1,而是加减任意整数。
3.支持进程退出时回滚对信号量的修改。
信号的互斥功能能够用于多进程共享某个资源并且可能同时对该项资源进行操作,利用互斥性可以维护资源数据的一致性。也可以实现同步变成,当某个进程A需要等待另一个进程B先执行一些操作才能够执行,这时候可以建立一个信号量初始值为0,表示当前可用资源数目为0,进程A执行时先进行P操作获取资源,此时资源数目为0,A阻塞,B执行时候,首先执行必须的功能代码,然后对信号量调用V操作,信号量值变成1,此时被阻塞的进程A将被激活继续执行。这部分相对以前感觉非常难以理解,还是看代码。
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/sem.h>
typedef union semun
{
int val;//整形变量,保存信号量值
struct semid_ds *buf;//信号控制结构变量指针
ushort *array;//无符号短整形指针变量
} SEMCTL_UNION;
int main()
{
int n,semid;//信号量标识符,保存信号量的值
key_t semkey;//键值
SEMCTL_UNION semctl_arg;//共用体变量
struct sembuf buf;//进行semop调用时所需要的结构体
if((semkey = ftok("/etc/profile",1)) < 0)//生成键值
{
perror("ftok");
exit(1);
}
if((semid = semget(semkey,1,0)) < 0)//获取信号量标志符,,包含一个信号,第三个参数是0表明获取已经存在的信号量描述符,第二个参数必须和实际的一致
{
perror("semget");
exit(2);
}
semctl_arg.val = 2;//初始值设置为2
if (semctl(semid,0,SETVAL,semctl_arg) < 0)//设置信号量的初始值
{
perror("semctl");
exit(3);
}
memset(&buf,0x00,sizeof(struct sembuf));//清空结构体
buf.sem_num = 0;//信号量在信号量集中的序列,由0开始,代表第一个信号量,这里也就只有一个信号量
buf.sem_op = -1;//信号量操作的值
buf.sem_flg = IPC_NOWAIT;//非阻塞模式
for(n=0;;n++)//循环调用P操作,直到信号量变成0则中断循环
{
if(semop(semid,&buf,1) == -1)//调用P操作
{
perror("semop");
break;
}
//获得并且输出信号量的值
printf("semop[%d]:current semphore value=%d\n",n,semctl(semid,0,GETVAL,semctl_arg));
}
return 0;
}