您的位置:首页 > 运维架构 > Linux

Unix/Linux下的IPC---共享内存

2013-09-15 13:30 281 查看
进程间需要共享的数据存放在一个叫做IPC共享内存区域的地方,所有需要访问这些共享数据的进程都要把存放这些共享数据的共享内存区域以及所对应的物理内存页面的全部地址空间都映射到自己的进程地址空间中

TAG: 共享内存 IPC 进程间通讯
原文地址:http://www.rosoo.net/a/201002/8465.html

共享内存指的是:把所有需要使用的共享数据都存放在共享内存(IPC shared memory region)区域中,任何想要访问这些共享数据的进程都必须在自己的进程地址空间中新增加一块内存区域,用来映射存放共享数据的物理内存页面;也就是说,任何想要访问这些共享数据的进程都必须把存放共享数据的物理内存页面的全部地址空间都映射到自己的进程地址空间中;这就是System V共享内存的实现机制;

系统调用mmap()通过映射一个普通文件来实现共享内存;而System V则是通过映射特殊文件系统shm中的一个文件来实现进程间共享内存通信的;也就是说,每个共享内存区域都对应特殊文件系统shm中的一个文件(通过struct shmid_kernel结构来关联起来的);

一、System V共享内存的原理:

进程间需要共享的数据存放在一个叫做IPC共享内存区域的地方,所有需要访问这些共享数据的进程都要把存放这些共享数据的共享内存区域以及所对应的物理内存页面的全部地址空间都映射到自己的进程地址空间中.System V共享内存通过系统调用shmget()来获得或创建一个IPC共享内存区域,并返回这个共享内存区域的唯一标识符.内核在保证系统调用shmget()获得或创建一个共享内存区域,并初始化该共享内存区域对应的shmid_kernel结构的同时,还将在特殊文件系统shm中创建或打开一个同名的文件,并在内存中建立起该文件的struct
dentry和struct inode结构,新建的文件不属于任何一个进程(任何进程够可以访问该共享内存区域).所有这一切操作都是系统调用shmget()完成的;

注意:每一个共享内存区域都有一个控制结构:struct shmid_kernel,shmid_kernel结构是共享内存区域中非常重要的一个结构,它是把存储管理与文件系统结合起来的桥梁;struct shmid_kernel结构的定义如下:

struct shmid_kernel

{

struct kern_ipc_perm shm_perm;

struct file* shm_file;

int id;

unsigned long shm_nattch;

unsigned long shm_segsz;

time_t shm_atim;

time_t shm_dtim;

time_t shm_ctim;

pid_t shm_cprid;

pid_t shm_lprid;

};

该结构中最重要的一个字段就是shm_file,它存储了将被映射文件的地址,每个共享内存区域对象都对应特殊文件系统shm中的一个文件.一般情况下,特殊文件系统shm中的文件是不能使用read()、write()函数进行访问的,当采用共享内存机制把其中的文件映射到进程的地址空间中之后,可以直接采用访问内存的方式对其进行访问;

与System V共享内存相关的数据结构,如图:



正如消息队列和信号量集一样,系统内核通过数据结构struct ipc_ids shm_ids来维护系统中所有的共享内存区域对象.上图中的shm_ids.entries变量指向一个ipc_id结构数组,而每个ipc_id数组结构中都有一个指向kern_ipc_perm结构的指针,对于System V来说,kern_ipc_perm结构的宿主就是struct shmid_kernel结构,而shmid_kernel结构是用来描述一个共享内存区域对象的,这样内核就能控制到系统中的所有共享内存区域对象.同时,在shmid_kernal结构中的struct
file类型的指针指向特殊文件系统shm中相应的文件,这样,一个共享内存区域对象就与特殊文件系统shm中的一个文件关联起来了;

二、映射文件地址空间到进程地址空间:

在创建或打开一个共享内存区域对象之后,还要把它映射到进程的地址空间中,系统调用shmat()用来完成这项任务;由于在调用shmget()时就已经在特殊文件系统shm中创建了一个同名文件与该共享内存区域对象对应,因此,调用shmat()的过程相当于把特殊文件系统shm中的同名文件映射到进程地址空间的过程,原理与mmap()大同小异;

三、System V共享内存的系统API:

System V共享内存主要有以下几个API:shmget()、shmat()、shmdt()和shmctl();

#include <sys/ipc.h>

#include <sys/shm.h>

shmget()用来获得或创建共享内存对象并返回该共享内存对象的ID的,如果指定的共享内存不存在,就创建相应的共享内存区域.

shmat()用来把共享内存区域的地址空间映射到进程地址空间中,这样,进程就能方便地对共享内存区域进程访问了.

shmdt()用来解除共享内存区域对象与进程地址空间的映射关系,也就是解除进程对共享内存区域的映射.

shmctl()实现对共享内存区域的控制操作.

注意:shmget()的内部实现包含了许多System V共享内存机制;shmat()在把共享内存区域映射到进程地址空间时,并不真正改变进程的页表.当进程第一次访问共享内存映射区域时,会因为没有物理页表的分配而导致一个缺页异常,然后内核再根据相应的存储管理机制为共享内存映射区域分配相应的物理页表;

四、System V共享内存限制:

在/proc/sys/kernel/目录下记录着System V共享内存机制的一些限制;比如:一个共享内存区域的最大字节数shmmax,系统范围内最大共享内存区标识符数shmmni,等等;可以手工对其进行调整,但是不推荐这样做;

五、System V共享内存机制与内存映射机制的区别:

1、System V共享内存中的数据从来不会写入到实际磁盘文件中去;而通过系统调用mmap()映射普通文件实现的共享内存通信机制可以指定何时把共享内存中的数据写入到实际的磁盘文件中;注:System V是通过映射特殊文件系统shm中的文件来实现的,特殊文件系统shm的安装点是在系统的交换分区上,系统内核重新启动并引导之后,所有的内容全部丢失;

2、System V共享内存区域对象是随内核持续的,即使所有访问共享内存区域对象的进程都已经正常结束,共享内存区域对象仍然在内核中存在(除非显式删除共享内存区域对象),在内核重新引导之前,对该共享内存区域对象的任何改写操作都将一直保留;简单地说,共享内存区域对象的生命周期跟系统内核的生命周期是一致的,而且共享内存区域对象的作用域范围就是在整个系统内核的生命周期之内;

3、通过调用mmap()映射普通文件进行进程间通信时,一定要注意考虑"进程何时终止"对通信的影响.而通过System V共享内存实现的进程间通信则不然.

六、结论:

共享内存允许两个或多个进程共享同一个给定的存储区,因为不需要来回地复制数据,所以,它是最快的一种进程间通信机制;共享内存可以通过mmap()映射普通文件(特殊情况下还可以采用匿名映射)的机制实现,也可以通过System V的共享内存机制实现.应用接口和原理很简单,内部机制复杂.为了实现更安全的通信,往往还与信号灯等同步机制共同使用;

共享内存涉及到存储管理和文件系统等方面的知识,深入理解其内部机制有一定的难度,关键还是要紧紧抓住内核使用的重要数据结构.System V共享内存是以文件的形式组织在特殊文件系统shm中的.通过shmget()可以创建或获得共享内存标识符.取得共享内存标识符后,要通过shmat()把这个共享内存区域映射到本进程的地址空间中;
(秩名)

本站文章除注明转载外,均为本站原创或编译欢迎任何形式的转载,但请务必注明出处,尊重他人劳动,同学习共成长。转载请注明:文章转载自:罗索实验室 [http://www.rosoo.net/a/201002/8465.html]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: