IPC 进程间通信方式——共享内存
共享内存
- 共享内存区域是被多个进程共享的一部分物理内存。
- 多个进程都可以把共享内存映射到自己的虚拟空间。所有用户空间的进程要操作共享内存,都要将其映射到自己的虚拟空间,通过映射的虚拟内存空间地址去操作共享内存,从而达到进程间的数据通信。
- 共享内存是进程间共享数据的一种最快的方法,一个进程向共享内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容
- 本身不提供同步机制,可通过信号量进行同步
- 提升数据处理效率,一种效率最高的IPC机制
共享内存属性信息
struct shmid_ds{
struct ipc_perm shm_perm;
size_t shm_semsz;//共享内存大小
pid_t shm_lpid;//最后一次调用系统pid
pid_t shm_cpid;//pid的创建者的id
shmatt_t shm_nattch;//和共享内存成功映射的数量
time_t shm_atime;//最后一个成功映射的时间
time_t shm_dtime;//最后断开映射的时间
time_t shm_ctime;//最后改变的时间
}
共享内存使用步骤
- 使用shmget()函数创建共享内存
- 使用shmat()函数映射共享内存,将这段创建的共享内存映射到具体的进程虚拟内存空间
创建共享内存
#include <sys/shm.h>
itn shmget(key_t key,size_t size,int shmflag);
//返回,成功返回内核中共享内存的表示iD,失败返回-1.
- 参数
- key:用户指定的共享内存键值
- size:共享内存大小
- shmflg:IPC_CREAT,IPC_EXCL等权限组合
- erron
- EINVAL(无效的内存段大小)
- EEXIST(内存段已经存在,无法创建)
- EIDRM(内存段已经被删除)
- ENOENT(内存段不存在)
- EACCES(权限不够)
- ENOMEM(没有足够内存创建内存段)
共享内存控制
#include<sys/shm.h>
int shmctl(int shmid,int cmd,struct shmid_ds *buf);
- 参数
- shmid:共享内存ID
- buf:共享内存属性指针
- cmd
- IPC_STAT 获取共享内存段属性
- IPC_SET 设置共享内存段属性
- IPC_RMID 删除共享内存段
- SHM_LOCK 锁定共享内存段页面
- SHM_UNLOCK 解锁锁定
共享内存映射和解除
#include<sys/shm.h>
void shmat(int shmid,char *shmaddr,int shmflg);
int shmdt(char *shmaddr);
//失败返回-1
- 参数
- shmid:共享内存ID
- shmaddr:映射到进程虚拟内存空间的地址,系统自动分配
- shmflg:弱shmaddr为0,shmflag也是0
- SHM_RND
- SHMLBA 地址为2的次方
- SHM_RDONLY 只读方式连接
- errno
- EINVAL 无效的IPC ID值或无效的地址
- ENOMEM 没有足够的内存
- EACCES 权限不够
- 子进程不继承父进程创建的共享内存,大家是共享的,子进程继承父进程映射的地址。
案例:
通过共享内存实现进程间通信
#include <sys/shm.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "tell.h"
int main(void)
{
//创建共享内存
int shmid;
if((shmid=shmget(IPC_PRIVATE,1023,
IPC_CREAT|IPC_EXCL|0777))<0)
{
perror("shmget error");
exit(1);
}
pid_t pid;
init();//初始化管道
if((pid=fork())<0)
{
perror("fork error");
exit(1);
}else if(pid>0)
{
//parent process
//进行共享内存的映射
int *pi=(int*)shmat(shmid,0,0);
if(pi==(int*)-1)
{
perror("shmat error");
exit(1);
}
//往共享内存中写入数据
*pi=100;
*(pi+1)=200;
//操作完毕,解除映射
shmdt(pi);
//通知子进程去读取数据
notify_pipe();
destory_pipe();
wait(0);
}else
{
//child process
//子进程阻塞,都带父进程往共享内存写入数据
wait_pipe();
//子进程从共享内存中读取数据
//子进程进行共享内存的映射
int *pi=(int*)shmat(shmid,0,0);
if(pi==(int*)-1)
{
perror("shmat error");
exit(1);
}
printf("start:%d,end:%d\n",*pi,*(pi+1));
//读取完毕解除映射
shmdt(pi);
//删除共享内存
shmctl(shmid,IPC_RMID,NULL);
destory_pipe();
}
}