Linux中IPC机制:共享内存区
来源:互联网 发布:最好吃的方便面知乎 编辑:程序博客网 时间:2024/06/09 20:47
前言
研究IPMI源码,实现将IPMI获取的性能数据传递给另一独立进程,主要用到的IPC方法就是共享内存。现在来说,基本是对共享内存这一机制有了更加透彻的理解和掌握。记录下。相关背景:
Linux中IPC有几种实现版本,例如共享内存就有Posix共享内存与System V共享内存,它们的联系与区别?答:Linux中的进程间通信机制源自于Unix平台上的进程通信机制。Unix的两大分支AT&T Unix和BSD Unix在进程通信实现机制上的各有所不同,前者对Unix早期的IPC进行系统的改进和扩充,形成了运行在单个计算机上的System V IPC,后者则跳过单机限制,实现了基于socket的进程间通信机制。同时因为Unix版本的多样性,IEEE又单独制定了一套独立的Posix IPC标准。在这三者基础上,Linux所继承的IPC,如图示:
也就是说,Linux上的IPC版本之间只是实现不同,效果并没有区别。
共享内存区
关于共享内存机制具体的介绍我就不多说了,内容网上多的是,有书的话请详细阅读《网络编程,卷二》。这里,我先后使用过Mmap映射同一文件方式与System V API方式分别进行了实验,就其中遇到的一些问题说一说。
1,Mmap 映射
1)指定映射文件
适用于两个独立的任何进程,通过指定同一个文件来实现共享内存方式的进程间通信。需要注意的是,进程之间在共享内存时,共享内存会一直保持到通信完毕为止,数据内容会一直在内存中。在解除映射之后,内存上的内容才写回文件。
原理:所映射文件的实际数据成了被共享内存区的内容。那如何保证各个独立进程寻址到同一内存页面?
<1> page cache及swap cache中页面的区分:一个被访问文件的物理页面都驻留在page cache或swap cache中,一个页面的所有信息由struct page来描述。struct page中有一个域为指针mapping ,它指向一个struct address_space类型结构。page cache或swap cache中的所有页面就是根据address_space结构以及一个偏移量来区分的;
<2> 文件与address_space结构的对应:一个具体的文件在打开后,内核会在内存中为之建立一个struct inode结构,其中的i_mapping域指向一个address_space结构。这样,一个文件就对应一个address_space结构,一个address_space与一个偏移量能够确定一个page cache 或swap cache中的一个页面。因此,当要寻址某个数据时,很容易根据给定的文件及数据在文件内的偏移量而找到相应的页面;
<3> 进程调用mmap()时,只是在进程空间内新增了一块相应大小的缓冲区,并设置了相应的访问标识,但并没有建立进程空间到物理页面的映射。因此,第一次访问该空间时,会引发一个缺页异常;
<4> 对于共享内存映射情况,缺页异常处理程序首先在swap cache中寻找目标页(符合address_space以及偏移量的物理页),如果找到,则直接返回地址;如果没有找到,则判断该页是否在交换区(swap area),如果在,则执行一个换入操作;如果上述两种情况都不满足,处理程序将分配新的物理页面,并把它插入到page cache中。进程最终将更新进程页表;
<5> 所有进程在映射同一个共享内存区域时,情况都一样,在建立线性地址与物理地址之间的映射之后,不论进程各自的返回地址如何,实际访问的必然是同一个共享内存区域对应的物理页面;
以上原理出自参考的博文,对整个流程的理解,我用图表示:
实例仍然给出原博文例子,算是属于手抄版吧。。。反正是实例嘛,说明效果就好了。
/* example A */#include <sys/mman.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <string.h>typedef struct{ char name[4]; int age;}people;void main(int argc, char** argv) // map a normal file as shared mem:{ int fd,i; people *p_map; char temp; fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 00777); // 注意O_TRUNC选项参数 lseek(fd, sizeof(people)*5-1, SEEK_SET); write(fd, "", 1); p_map = (people*) mmap( NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0 ); close( fd ); temp = 'a'; for(i = 0; i < 10; i++) { temp += 1; memcpy( (p_map+i)->name, &temp, 1 ); (p_map+i)->age = 20+i; } printf(" initialize over \n "); sleep(10); munmap( p_map, sizeof(people)*10 ); printf( "umap ok \n" );}
/* example B */#include <sys/mman.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>typedef struct{ char name[4]; int age;}people;void main(int argc, char** argv) // map a normal file as shared mem:{ int fd,i; people *p_map; fd=open( argv[1], O_CREAT|O_RDWR, 00777 ); p_map = (people*)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); for(i = 0; i < 10; i++) { printf( "name: %s age %d;\n",(p_map+i)->name, (p_map+i)->age ); } munmap( p_map, sizeof(people)*10 );}
这里需要注意的是open()文件时O_TRUNC的参数。这个每次打开文件时清零文件的,也就是说,如果有三个以上的进程需要访问此文件,打开时就会把之前两个进程的数据给清零掉,这是我们不愿意看到的。这个问题在一开始时没注意,出了问题还以为是共享内存本身机制的问题,后来逐一对照代码,才发现原因所在。刚学习的同学尤其注意,实例代码也不能拿来就用,还是要自己搞明白才好。
另外需要说明的是,我没有遇到原文中提到的进程B在未解除映射时和解除映射后对共享内存的访问时候的差异,实在没找的原因。对文件大小和映射内存大小的限制也没有体现。贴上实例效果,我在想是否有可能是现在的内核版本中已经完善,就是只要不跨越内存页,超过文件大小的共享内存区域依然能够访问。
2)匿名映射文件
适用于具有亲缘关系的父子进程。原理同上。
所谓的匿名映射文件,只不过指定文件映射的一种特殊形式,本质上并无不同。这种形式主要就是基于Linux中父子进程创建的方式。父进程通过在调用fork()函数创建子进程之前,通过mmap()创建共享内存区,之后fork的子进程继承父进程资源,自然也就共享父进程mmap后的映射空间了。
直接贴上手抄版实例:
/* fork example */#include <sys/mman.h>#include <sys/types.h>#include <fcntl.h>#include <unistd.h>#include <stdio.h>#include <stdlib.h>#include <string.h>typedef struct{ char name[4]; int age;}people;void main(int argc, char** argv){ int i; people *p_map; char temp; p_map = (people *)mmap(NULL, sizeof(people)*10, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); if (fork() == 0) { sleep(2); for (i = 0;i < 5;i++) { printf("child read: the %d people's age is %d\n", i+1, (p_map+i)->age); } p_map->age = 100; munmap(p_map, sizeof(people)*10); //实际上,进程终止时,会自动解除映射。 exit(1); } temp = 'a'; for (i = 0; i < 5;i++) { temp += 1; memcpy((p_map+i)->name, &temp, 1); (p_map+i)->age = 20+i; } sleep(5); printf("parent read: the first people,s age is %d\n", p_map->age); printf("umap\n"); munmap(p_map,sizeof(people)*10); printf("umap ok\n");}
3) 对Mmap()返回地址的访问
这一块主要东西我觉得还是看《卷二》吧,其中的内容其实也还好,容易理解。
2,System V API
System V学习起来相对很简单,因为你只要会用接口就成。原理很mmap()文件映射是一样一样的。
总结
总的来说,共享内存对于数据的存取效率是很高效的。但目前为止,我觉得对于数据类型比较多,且结构复杂,像数组嵌套之类的用这个太蛋疼了。。因为我只知道一个共享内存的首地址,所以对内存的访问只能一个萝卜一个坑的去找。很不爽。不过,也可能是目前见识有限,暂就这些吧。
另外,真心觉得写这种技术型文章很费精力啊。。远不是贴两个程序,说两句话就成的。这方面,我做的不够。
主要参考:
博文:Linux环境进程间通信(五): 共享内存(上)
《网络编程,卷二》
- Linux中IPC机制:共享内存区
- linux ipc机制-共享内存
- linux IPC---共享内存
- IPC机制---共享内存编程
- linux下的IPC机制之共享内存
- Linux IPC之共享内存
- Linux IPC之共享内存
- Linux-IPC之共享内存
- Linux IPC之共享内存
- Linux C编程--进程间通信(IPC)5--System V IPC 机制3--共享内存
- Linux C编程--进程间通信(IPC)5--System V IPC 机制3--共享内存
- Linux IPC实 --共享内存/内存映射
- Linux共享内存机制
- Linux 基于IPC机制实现进程间的共享内存处理
- Linux下IPC共享内存通信
- Linux XSI IPC 之共享内存
- linux SysV IPC shm共享内存实现
- Unix/Linux下的IPC---共享内存
- wiki插件安装步骤2012-11-28
- 分享bootstarp3.0.2响应式模板-第二弹[Metronic_conquer]
- 黑马程序员_String类、集合总结
- 线程编程指南 Threading Programming Guide (2)[苹果官方文档的翻译]
- 浅谈五大Python Web框架
- Linux中IPC机制:共享内存区
- vim常用技术
- 上手炫酷狂拽的Sublime Text
- C指针原理(38)-gtk
- 链表
- classload
- Windows Store app 开发语言
- Merge k Sorted Lists 合并k个有序链表@LeetCode
- 游戏服务端之xml类型转换