进程间通信——共享内存

  一、共享内存

           共享内存是IPC提供的另一种进程间通信机制。共享内存是通过将内核分配的共享存储区映射到进程的地址空间实现的。它允许两个或者多个进程共享一个给定的存储区。由于数据不需要在客户进程和服务器进程之间复制,所以这是最快的一种IPC.

      两个进程通过共享内存通信是指:同一块物理内存被映射到多个进程各自的进程空间,各个进程都可以对共享内存中的数据进行更新。

二、共享内存的映射

    共享内存创建以后,用户程序在运行时,通过调用IPC接口函数将该存储区映射至应用程序的内存地址空间。成功映射后对共享内存的读写就像读写进程的比变量一样快捷。映射后的进程地址空间如图所示:

进程间通信——共享内存

进行共享内存映射时,可以映射到任意的数据结构。例如可以映射到程序中的一个普通的字符数组,也可以映射到程序自定义的一个数据结构。

三、共享内存的使用

进程间通信——共享内存

 1 、共享内存的创建:要使用共享内存,首先应该创建共享内存。在linux系统下,由函数shmget实现

          #include<sys/shm.h>

        函数原型:   int    shmget(   key_t   key,  size_t   size,   int  flag);//返回值:成功 共享内存ID,出错,-1

        参数说明:key_t   key: 输入参数,键值;将key变换成一个表示符的规则,以及创建一个新共享存储段,还是                            引用一个已有的共享存储段。当创建一个新的共享存储段时,初始化shmid_ds结构的下列成员。

       【注】:内核为每个共享存储段维护一个结构,该结构至少要为每个共享存储段包含以下成员:

                       struct  shmid_ds{

                                  struct ipc_perm   shm_perm;  //IPC许可权限结构,与消息队列相同,是每个IPC对象都有的。

                                  size_t                shm_segsz; // 共享内存的大小,以字节为单位

                                  pid_t                  shm_lpid;  //最后一次操作共享内存的进程ID

                                  pid_t                   shm_cpid;//创建共享内存的进程ID

                                 shmatt_t             shm_nattch;

                                 time_t                 shm_atime;

                                 time_t                 shm_dtime;

                                 time_t                shm_ctime;

                                            ···

                                       };


                          size_t   size:  输入参数,要创建的共享内存的大小,单位为字节

                         int  flag  :输入参数,创建共享内存标志

2、映射共享内存:

      共享内存创建成功后,在整个操作系统是全局可见的。只要具备访问权限,应用程序都可以使用此共享内存。在使用共享内存时,要将共享内存映射到本地进程的地址空间。函数shmat可以实现此功能

   函数原型;  void *shmat( int  shmid,   const  void *addr ,    int  flag);  //返回值; 成功,指向共享存储段的指针,出错-1

   参数说明:int  shmid :输入参数,共享内存的标识符,由、shmget返回

                     const  void *addr: 该参数是由调用shmat的用户进程指定的一个虚拟空间地址,新创建的共享内存将附                                                      在该地址后。该地址一般情况下直接指定为NULL

                    int  flag  :创建共享内存的标志

3、函数对共享内存的操作:

      共享内存创建成功后,系统在内核为其分配了一个类型为struct  shmid_ds的控制结构。IPC提供了函数获取该结构并通过修改该结构控制共享内存,这个函数就是shmctl。

      函数原型:   int  shmctl ( int   shmid,  ,int  cmd,   struct  shmid_ds  *buf);//返回值; 成功,返回0,出错-1

      参数说明: int  shmid :输入参数,共享内存的标识符,由、shmget返回

                         struct  shmid_ds  *buf  :输入或输出参数,指向shmid_ds结构的指针。在cmd参数为IPC_STAT时,该参数用作输出;在cmd参数为IPC_SET时,该参数用作输入。

                         int  cmd :输入参数,控制命令(5种)

4、删除共享内存映射:

      当对共享内存的操作结束,应该调用函数shmdt解除共享内存与进程地址空间的映射。注意:这里并不是从系统中删除其标识符以及相关的数据结构。该标识符仍然存在,直至某个进程(一般是服务器进程)带IPC_RMID命令的调用shmctl特地删除它为止。

      函数原型: int  shmdt(  const   void*  adrr);  // 返回值:若成功,返回0,若出错,返回-1

      参数说明:adrr :输入参数,共享内存映射至本地的地址,它是由shmat返回的地址

:与其他IPC机制相同,共享内存也有键值和标识符。键值的生成使用与消息队列相同。与消息队列不同的是,共享内存有引用计数器机制。每有一个进程进行一次共享内存映射,共享内存计数器就会加1.而每调用shmdt只是将该共享内存的引用计数器减1,只有引用计数器值变为0的情况下,才可以调用shmctl将共享内存真正地删除。可以通过执行命令ipcs -m的输出项nattch查看共享内存的访问计数。