1、信号量描述
信号量是一个特殊的变量,一般取正数值,他的值代表允许被访问的资源数目,获取资源时,要对信号量的值进行原子减1,该操作被称为p操作,当信号量的值为1时,代表没有资源可用,P操作会堵塞,释放资源时,需要对信号量的值+1,称为v操作,信号量主要用来同步进程
临界资源:同一时刻,只允许被一个进程或线程访问的资源//可以是一个虚拟机,我正在使用别人就不行,也可以是一个buff,都有可能是一个临界资源
临界值(区):访问临界资源的代码段
不去执行临界区的代码就不会访问临界资源//保护临界资源实际是通过保护临界区来实现的
P操作:获取资源 原子+1
V操作:释放资源 原子-1
信号量的值如果只取0.1,将其称为二值信号量//只标识信号可用不可用
如果信号量的值大于1,称为计数信号量//计算信号量的个数
可使用资源的数量
利用信号量来限制对于资源的访问
2、信号量使用
是由内核管理的
操作信号量的接口介绍
semget()//创建或者获取已经存在的信号量
成功的话返回信号量的ID,失败返回-1;
key:两个进程使用同一个key,就可以使用同一个信号量
naems:内核维护的是一个信号量集,在创建信号量时,其指定信号量集中信号量的个数
semflg可选IPC_CREAT IPC_EXCL
3、临界资源
当两个进程需要调用一个临界资源,调用时,必须先判断,信号量的值是0 还是1,如果信号量为0,表示临界资源被占用,只有等到信号量为1,才能使用这个临界资源,如果信号量为1,那么先执行p操作,等使用完资源之后,再进行V操作
4、操作函数
-
sem_init()//创建信号量
void sem_init() { semid =semget((key_t)1234,1,IPC_CREAT|IPC_EXCL|0600) //创建信号量 第一参数 数目 全新创建一个信号量,如果信号量已经参在就返回失败 权限 if(semid==-1)//已经创建过了//可能存在 { semid semget(key_t)1234,1,0600)//只要(key_t)1234 和创建好的信号量保持一致就可以 //如果信号量已经存在,就没有改的意义了 if(semid==-1) { printf("semget err");//被别人创建,实在创建不了 } } else//初始化,成功创建之后 { union semun a; a.val=1;//给信号量赋初值为1 if(semctl(semid,0,SETVAL)==-1) { printf("semctl setval err\n"); } } }
IPC_CREATPIC_EXCLPIC_EXCL//全新创建一个信号量,如果信号量存在,返回失败
-
sem_p()
void sem_p { struct sembuf buf; buf.sem_num=0;//找到第0 个元素 buf.sem_op=-1;//给找到的元素-1 buf.sem_flg=SEM_UNDO; if(semop(semid,&buf,1)==-1)//说明操作有问题 {// 全局变量 地址 数量 printf("op p err\n"); } }
-
sem_v()
void sem_v { struct sembuf buf; buf.sem_num=0;//找到第0 个元素 buf.sem_op=1;//给找到的元素+1 buf.sem_flg=SEM_UNDO; if(semop(semid,&buf,1)==-1)//说明操作有问题 {// 全局变量 地址 数量 printf("op v err\n"); } }
-
sem_destroy();//销毁操作
void sem_destroy() { if(semctl(semid,0,IPC_RMID)==-1)//封装失败 { printf("semctl rm err\n"); } }
销毁不是把一组信号量中的一个进行销毁,是把一组信号量全部销毁
-
两个进程访问同一个临界区信号量,在后台执行,在访问信号量之间,都进行初始化,访问临界区之前进行P操作,访问后进行V操作,最后都执行销毁操作,全新创建和销毁只有一次
-
结束后通过ipcs查看信号有没有被移除
5、共享内存
当我们申请一块内存为共享空间,两个进程映射到物理内存有一块内存是重合的们可以进行进程间的通信,能看到同一块内存空间
1、先创建共享内存
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/shm.h>
int main()
{
int shmid=shmget((key_t)1234,128,IPC_CREAT|IPC_EXCL|0600)
if(shmid==-1)
{
printf("shmid err");
exit(1);
}
exit(0);
}
2、再将共享内存映射到逻辑空间中
char*s=(char*)shmat(shmid,NULL,0)//获取指向共享内存的指针
if(s==(char*)-1)//判断是否映射成功
{
printf("shmat err\n");
exit(1);
}
strcpy(s,"hello");//将数据拷贝到共享内存中
3、断开映射//取消对共享内存的使用
shmdt(s);
内核会一直维护,进程终止后,不会影响到共享内存,共享内存依旧会使用