进程间通信---共享内存&shmget

介绍这一部分主要从它的几个函数入手:

概念:共享内存是在物理内存上开辟一块区域,这段被多个进程映射到自己进程的虚拟地址空间上,这些进程就可以直接访问该共享内存区域,从而通过该区域实现各进程间的通信。共享内存是进程间最快的一种通信方式,一个进程向共享内存上面写数据,共享这块内存的所有进程都可以看到其中的内容,这块共享内存的页面,出现在所用共享该页面进程的页表中,给人一种就是在访问自己地址空间里面的数据一样。共享内存映射图:

可以看到进程A和进程B共享了一块内存,分别将共享内存所在的物理页加入到自己的页表中,访问时就像访问自己的东西一样,所以它是最快的一种通信方式。但是会有一个问题,就是可能存在多个进程同时访问这块区域,此时共享内存区域就成了临界资源,所以我们在使用共享内存时需要对它进行同步控制才能保证安全的使用。比如信号量、加锁等方式。

 

和其他IPC对象一样,共享内存对象的获取也是由key控制。每一个新创建的共享内存对象都用一个shmid_kernel的数据结构表示。系统中所用的shmid_kernel数据结构都保存在shm_segs向量表中,该向量表中的每一个元素都是一个指向shmid_kernel数据结构的指针。shimd_kernel会包含共享内存的所用信息。

 

Linux为共享内存提供的四种操作

1、shmget函数:创建或者获得一个共享内存对象。它和其他两种IPC机制一样,进程在使用共享内存区域之前,必须通过系统调用sys_ipc(call值为SHMGET)创建一个键值为key的共享内存对象,或者获得已经存在的键值为key的某共享内存对象的引用标识符。以后对共享内存对象的访问都是通过该引用标识符进行。看一下shmget函数定义:

int shmget(key_t key,int size,int shmflg)

这里key表示该共享内存的键值,size是该共享内存的大小(以字节为单位),shmflg是标志(对该共享内存对象的特殊要求)。

 过程如下:

  1. 计算size要占用的页数,检查其合法性
  2. 申请一块内存用于建立shmid_kernel数据结构,(这里申请的内存区域大小不包括真正的共享内存区,实际上,要等第一个进程试图访问它的时候才真正创建共享内存区。
  3. 将shmid_kernel数据结构加入到向量表shm_segs中的合适位置
  4. 返回该共享内存对象的引用标识符。

 

2、shmat函数:在获得共享内存区域的引用标识符后,还必须将共享内存区域映射到进程的虚拟地址空间中,然后才能使用该共享内存区域。系统调用sys_IPC(call值为SHMAT)用于共享内存区到进程虚拟地址空间的映射。看一下shmat函数的定义:

void * shmat(int shmid,const void *shmaddr,int shmflg);

shmid:是shmget返回的共享内存对象的引用标识符

shmaddr:用来指定该共享内存在进程虚拟地址空间对应的虚拟地址(如果是0则由系统分配,一般从1G开始)

shaflg:映射标志

返回值:进程的虚拟地址

过程如下:

  1. 根据shmid找到共享内存对象
  2. 检查shmaddr的合法性(不能超过3G)
  3. 申请一块内存用于创建数据结构vm_area_struct,填写该结构
  4. 将其加入到进程的mm结构和该共享对象的vm_area_atruct中

注意:

当进程第一次访问共享虚拟内存的某页时,因为所有的共享内存页都还没有分配,所以会发生一个page fault异常。然后Linux处理这个缺页异常,将分配一个物理页,并为它创建一个页表条目。这个不仅进入当前进程的页表,同时也存到shmid_kernel数据结构的页表shm_pages中。

当下一个进程访问这块内存时,发生page fault的时候,在查看shmid_kelnel数据结构的shm_pages时,发现共享页已经存在,它只需要把其中的页表项填入到自己进程页表的相应位置即可,而不需要重新创建物理页,所以,是第一个访问共享内存的进程创建物理页后其他访问它的进程只需要把该页加入到自己的虚拟地址空间就好了。

 

3、shmdt函数:当进程不需要共享这块内存时,更新自己的页表,共享内存对应的虚拟内存页被标记为无效。当最后一个进程分离这个共享内存时,共享内存页被释放,同时共享内存的shmid_kernel数据结构也被释放。Shmdt函数定义:

int shmdt(char *shmaddr)

shmaddr:表示进程要分离共享页的起始虚拟地址。

该函数搜索内存中所用的vm_area_atruct数据结构,找到地址shmaddr对应的那个,调用函数将其释放,更新进程表中对应的页表项。

 

4、Shmctl函数:操作包括获得共享内存对象的状态、释放共享内存对象资源等。这里涉及到了同步控制问题。

下面看一下代码示例:

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
1.创建共享内存:
int shmget(key_t key,int size,int shmflg);
参数说明:
key:用来表示新建或者已经存在的共享内存去的关键字。
size:创建共享内存的大小。
shmflg:可以指定的特殊标志。IPC_CREATE,IPC_EXCL以及低九位的权限。
eg:
int shmid;
shmid=shmget(IPC_PRIVATE,4096,IPC_CREATE|IPC_EXCL|0660);
if(shmid==-1)
perror("shmget()");

2.连接共享内存
char *shmat(int shmid,char *shmaddr,int shmflg);
参数说明
shmid:共享内存的关键字
shmaddr:指定共享内存出现在进程内存地址的什么位置,通常我们让内核自己决定一个合适的地址位置,用的时候设为0。
eg:
int shmid;
char *shmp;
shmp=shmat(shmid,0,0);
if(shmp==(char *)(-1))
perror("shmat()\n");

3.分离共享内存:当程序不再需要共享内后,我们需要将共享内存分离以便对其进行释放,分离共享内存的函数原形如下:
int shmdt(char *shmaddr);

4.释放共享内存
int shmctl(int shmid,int cmd,struct shmid_ds *buf);

参考博客:https://blog.csdn.net/yusiguyuan/article/details/45154921

猜你喜欢

转载自blog.csdn.net/ShWe_yayaya/article/details/81775624