进程间通信ipc(共享内存)

system V标准的进程间通信方式:
共享内存
消息队列
信号量
共享内存:是进程间通信方式中最快的一种
原理:进程间通信ipc(共享内存)
一个进程在访问内存时候都是通过虚拟地址空间地址来访问物理内存,每个进程都有自己的虚拟地址空间(mm),将物理内存映射过来,程序在存储变量时候在虚拟地址空间找一个地址并且在物理内存开辟一个物理内存区域然后有个页表经过页表将映射连接起来,共享内存指的是多个进程可以同时操作同一块内存,这块内存指的是物理内存,他们能同时共享一块物理内存,也就意味着在物理内存上面开辟一块空间,只要这个空间可以让多个进程能够共享到,都能访问到这块物理内存代表它们共享了,
怎么能让每一个进程都能访问到这块物理内存:通过页表映射到虚拟地址空间当中去选择一块地址把它映射了,那么就可以直接通过访问这块虚拟地址空间来访问这块物理内存了.其他的进程也是一样的道理如上图;右边的进程为进程b,也是通过页表映射过来,进程b在操作虚拟地址空间当中的那块地址相当于操作映射的物理内存,左边进程为b当a进程数据修改了则物理内存数据也修改了与之对应的b的数据也修改了,所以共享内存用于数据共享因为它们通过地址访问到同一块内存,并且这块内存直接通过虚拟地址空间当中的用户空间的地址来直接访问(加载是在共享区里面加载,共享区:程序运行起来后一些共享的数据)
为什么最快?
共享内存直接通过虚拟地址访问到内存直接修改就可以了,但是匿名管道是需要从用户态拷贝到内核态然后在从内核态拷贝到用户态,相比较共享内存少了两步拷贝的过程(匿名管道原理见上一篇)所以共享内存访问快;
共享内存就是直接通过操作虚拟地址对物理内存进行操作没有拷贝的过程
共享内存的操作:
操作步骤:
1.创建共享内存(物理内存)
2建立映射关系,将共享内存映射到进程虚拟地址空间
3直接通过虚拟地址访问
4解除映射
5删除共享内存
如何实现:
shmget:shm:共享内存,穿件一块共享内存
三个参数
int shmget(key_t key, size_t size, int shmflg);
key:表示符能够标识能让其他进程在打开共享内存的时候打开和我们创建的同一块共享内存key值就是共享内存在操作系统中的标识
key还有个值可以直接给IPC_PRIVATE(ipc私有,指的是这块共享内存是私有的代表和其他进程无法通信,意义为:应用在具有亲缘关系的进程间通信)
size_t size确定共享内存有多大,共享内存的大小
size实际上分配大小是按照内存页来分配
shmflag标志位
IPC_CREAT 不存在创建存在打开
IPC_EXCL存在报错不存在则创建
IPC_CREAT and IPC_EXCL 同
O_CREAT | O_EXCL
返回值:一个有效的段标识,标识可以称作为在进程当中对共享内存操作的句柄
key值给定可以通过宏自定义一个数字
或者可以通过ftok可以产生一个key
key_t ftok(const char *pathname, int proj_id);
第一个参数叫文件名第二个叫proj_id,proj_id是自己定义的一个数字
原理:找到这个文件获取这个文件的inode结点号(inode结点号唯一)然后和后面的那个pro_id合在一起生成一个key值,将我们的inode节点号取出一半proj_id取出一半然后把两个合在一起生成key值这种方法对文件的依赖性特别大如果文件被删除那么这个共享内存就获取不到了就打不开 了就算重新创建了一个文件但是inode结点号不一样;key值也不一样打开的共享内存也不是同一块了
#define IPC_KEY 0X12345678
7 int main(){
//共享内存的基本操作:
9 //创建:shmget
10 //映射:shmat atch
11 //对内存操作memrcp,strcop memset
12 //解触映射:shmdt
13 //删除:shmcl
14 //int shmget(key_t key, size_t size, int shmflg);
15 //key:共享内存在操作系统中的表示符 数字
16 //size共享内存大小32个字节
17 //shmflg IPC_CREAT不存在创建存在打开就好 IPC_EXCL如果存在了想让它退出就加上
18 //excl
19 //返回值:进程对共享内存的操作句柄失败返回-1
20 //通常key通常用宏随机定义一个数字
21 int shmid=shmget(IPC_KEY,32,IPC_CREAT);
22 //key_t ftok(const char *pathname, int proj_id);
23 //通过文件名(文件名随意给)+proj_id(自定义数字)
24 //通过问价的inode结点号与proj_id合在一起生成key值但是对文件
25 //依赖性大
if(shmid<0){
27 perror(“shmget error”);//如果是系统调用函数可以用perror
28 return -1;
29 }
30
31
32 return 0;
33 }
./shm
[[email protected] ipc]$ ipcs

--------- 消息队列 -----------
键 msqid 拥有者 权限 已用字节数 消息

------------ 共享内存段 --------------
键 shmid 拥有者 权限 字节 nattch 状态
0x12345678 65536 chenyongji 0 32 0

--------- 信号量数组 -----------

键 semid 拥有者 权限 nsems
ipcs查看操作系统上面的ipc信息
ipcs -m只看共享内存.
进程间通信ipc(共享内存)
可以看到上面的id是65536,key值为0x12345678(我们设置的)权限为0(代表对共享内存操作没权限)因为创建的时候没有指定权限nattch(当前有多少进程和共享内存建立了映射连接,状态为空
ipcrm -m 65536(shmid)
删除共享内存
int shmid=shmget(IPC_KEY,32,IPC_CREAT |0664);
增加权限后:
进程间通信ipc(共享内存)
进程退出共享内存依然存在,共享内存的生命周期是随操作系统内核的,在操作系统退出之后共享内存才会被销毁(也就是创建以了之后会一直存在于操作系统当中)
void *shmat(int shmid, const void *shmaddr, int shmflg);

什么时候删除共享内存:只要nattch(共享内存计数)为0才可以删除成功,shmctl并不是直接删除等待共享内存nattch也就是进程为0才会删除共享内存,并且在等待删除共享内存期间,其他进程如果在想映射会拒绝映射
共享内存用于数据共享它在写数据的时候以数据覆盖的形式进行写入的如果用memcpy则前面数据长后面数据少则会出现没有覆盖掉的情况
int main(){
8 int shmid=shmget(IPC_KEY,32,IPC_CREAT |0664);
9 if(shmid<0){
10 perror(“shmget error”);//如果是系统调用函数可以用perror
11 return -1;
12 }
13 void* shm_start=shmat(shmid,NULL,0);
14 if(shm_start==(void*)-1){
15 perror(“shmat error”);
16 return -1;
17 }
18 //映射成功
19 while(1){
20 printf(“shm[%s]\n”,shm_start);
21 sleep(1);
22 }
23 //解触映射
24 shmdt(shm_start);
25 shmctl(shmid,IPC_RMID,NULL);
26 return 0;

int main(){
/对内存操作memrcp,strcop memset
13 //解触映射:shmdt
14 //删除:shmcl
15 //int shmget(key_t key, size_t size, int shmflg);
16 //key:共享内存在操作系统中的表示符 数字
17 //size共享内存大小32个字节
18 //shmflg IPC_CREAT不存在创建存在打开就好 IPC_EXCL如果存在了想让它退出就加上
19 //excl
20 //返回值:进程对共享内存的操作句柄失败返回-1
21 //通常key通常用宏随机定义一个数字
22 int shmid=shmget(IPC_KEY,32,IPC_CREAT |0664);
23 //key_t ftok(const char pathname, int proj_id);
24 //通过文件名(文件名随意给)+proj_id(自定义数字)
25 //通过问价的inode结点号与proj_id合在一起生成key值但是对文件
26 //依赖性大
27 if(shmid<0){
28 perror(“shmget error”);//如果是系统调用函数可以用perror
29 return -1;
30 }
31 //共享内存映射到虚拟地址空间
//void shmat(int shmid, const void shmaddr, int shmflg);
33 //shmaddr:共享内存的操作句柄
34 //需要知道映射到虚拟地址空间的哪块地址上
35 //shmaddr用户指定需要印射的首地址(虚拟地址空间的需要映射的首地址
36 //通常给空,让操作系统分配,这个首地址是通过void
返回)
37 //shmflg:如果被指定为SHM_RDONLY–只读 0
38 //返回值映射首地址失败则返回(void
)-1
39 void
shm_start=shmat(shmid,NULL,0);
40 if(shm_start==(void*)-1){
41 perror(“shmat error”);
42 return -1;
43 }
44 //映射成功
45 while(1){
46 //操作直接给里面拷贝数据即可
47 printf(“please input:”);
48 fflush(stdout);
W> 49 scanf("%s",shm_start);//如果不用scanf会出先数据无法覆盖的情况
50 char buf[1024]={0};
51 memcpy(shm_start,buf,strlen(buf));
}
53 //解除映射
54 //int shmdt(const void *shmaddr);
55 //shmaddr:映射首地址
56 shmdt(shm_start);
57 //删除共享内存
58 //int shmctl(int shmid, int cmd, struct shmid_ds buf);
59 //第一个为操作句柄,映射首地址
60 //第二个为所指向的操作(获取共享内存的信息设置共享内存的信息),shmid_ds
buf就是> 共享内存的信息,对共享内存的操作IPC_RMID删除共享内存
61 //buf:用于获取共享内存的状态信息不想看置空就好
62 shmctl(shmid,IPC_RMID,NULL);
63 //如果删除共享内存并不会直接删除而是等待映射链接数为0并且在等待期间拒绝
64 //新的链接
65 return 0;
}