一、共享内存定义

    百度百科共享内存指在多处理器的计算机系统中,可以被不同中央处理器访问的大量内存。由于多个CPU需要快速访问存储器,这样就要对存储器进程缓存。任何一个缓存的数据被更新后,由于其他处理器也可能要存取,共享内存就需要立即更新,否则,不同的处理器可能用到不同的数据。

            在Linux系统中,共享内存允许一个进程或多个进程共享一个给定的存储区(共享内存)。不同进程之间共享的内存通常安排为同一段物理地址。进程可以将同一段共享内存连接到它们自己的地址空间中,所有进程都可以访问共享内存中的地址。这样的一种方式适合于数量级极大和数据结构极为复杂的进程间通信。但这种方式牺牲了系统的安全性,所以通常与其他进程间通信形式混合使用。

            注:共享内存并未提供同步机制,也就是说,在第一个结束对共享内存的与操作之前,并无自动机制可以阻止第二个进程开始对它进行读取。所以我们通常需要用其他的机制来同步对共享内存的访问。比如:信号量、互斥锁等。

二、使用方法

    1.创建共享内存

      调用接口shmget函数   

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

       参数 

       key:标识共享内存的键值:如果是IPC_PRIVATE用于创建当前进程的私有共享内存。

       size:共享内存的大小

       shmflg:设置共享内存的访问权限

       返回值:如果成功返回共享内存标识符,如果失败,返回-1。

        

            int main()
            {
                int shmid=shmget(IPC_PRIVATE,1024,0666);
                if(shmid<0)
                {
                    printf("error\n");
                }
                else
                {
                    printf("success\n");
                }
                return 0;
            }

     在命令行执行ipcs -m显示,已经成功创建了一块内存区

     Nattch字段显示已经附加到这个内存区的进程数。  

    2.影射共享内存

        

            void* shmat(int shmid, char *shmaddr, int flag)

    参数:

        shmid:shmget函数返回的共享内存标识符

        shmaddr:指定共享内存连接到当前进程中的地址位置

                                          它通常是一个空指针

                                           表示让系统来选择共享内存出现的地址

        flag:决定以什么样的方式来确定影射的地址(通常为0)

      返回值:如果成功,则返回共享内存映射到进程中的地址;

          如果失败,则返回-1.

                int main(int args,char *argv[])
                {
                    char *shmbuf=NULL;
                    int shmid=0;
                    if(arg>2)
                    {
                        shmid=atoi(argv[1]);
                        shmbuf=shmat(shmid,0,0);
                        if(atoi(argv[2]==1)
                        {
                            scanf("%s",shmbud);
                        }
                        if(atoi(argv[2])==2)
                        {
                            printf("%s",shmbuf);
                        }
                        shmdt(shmbuf);//将共享内存从进程中分离出去
                    }
                    return 0;
                }


    

    注意:通过  ipcs -m命令看到nattch字段显示有一个进程附加到共享内存。

      3.解除共享内存映射

      函数shmdt是将附加在shmaddr的段从调用进程的地址空间分离出去,这个地址必须是shmat返回的地址。

            int shmdt(char *shmaddr);

       函数调用成功返回0,失败返回-1。

       4.控制共享内存

        

        int shmctl(int shmid,int command,struct shmid_ds *buf);

     参数:

        shmid是shmget函数返回的共享内存标识符。

        command:要采取的操作,它可以取下面的三个值

            IPC_STAT:是把shmid_ds结构中的数据设置为共享内存的当前关联值,即用共享内存的当前关联值覆盖shmid_ds的值。

            IPC_SET:如果进程有足够的权限,就把共享内存的当前关联值设置为shmid_ds结构中给出的值

            IPC_RMID:删除共享内存段

        buf:buf是一个结构指针,它指向共享内存模式和访问权限的结构。

shmid_ds结构:

            struct shmid_ds
            {
                uid_t shm_perm.uid;
                uid_t shm_perm.gid;
                mode_t shm_perm.mode;
            };

            5.代码实例

            往共享内存写:

#include
#include
#include
#include
#include
#include
#define VALUE_SIZE 2048
struct share_use_st
{
    int write_flag;//作为一个标志,非0表示可读,0表示可写
    char value[VALUE_SIZE];//记录写入和读取的文本
};
int main()
{
   int run=1;
   void *shm=NULL;
   struct share_use_st *share=NULL;
   char buf[VALUE_SIZE+1];
   int shmid;
   shmid=shmget((key_t)123,sizeof(struct share_use_st),0666|IPC_CREAT);
   if(shmid==-1)
   {
       printf("%s\n",strerror(errno));
       exit(EXIT_FAILURE);
   }
   shm=shmat(shmid,NULL,0);
   if(shm==(void *)-1)
   {
       printf("%s\n",strerror(errno));
       exit(EXIT_FAILURE);
   }
   printf("\nMemory attached at %p\n",shm);
   //设置共享内存
   share=(struct share_use_st*)shm;

   while(run)
   {
      while(share->write_flag==1)
      {
          sleep(1);
          printf("waiting....\n");
      }
      printf("Enter some value:");
      read(STDIN_FILENO,buf,sizeof(buf)-1);
      strncpy(share->value,buf,VALUE_SIZE);
      share->write_flag=1;
      if(strncmp(buf,"end",3)==0)
      {
          run=0;
      }
   }
   if(shmdt(shm)==-1)
   {
       printf("%s\n",strerror(errno));
       exit(EXIT_FAILURE);
   }
   sleep(2);
   return 0;
}

        从共享内存读:

#include
#include
#include
#include
#include
#define VALUE_SIZE 2048
struct share_use_st
{
    int write_flag;//作为一个标志,非0表示可读,0表示可写
    char value[VALUE_SIZE];//记录写入和读取的文本
};
int main(int args,char *argv[])
{
    int run=1;
    void *shmbuf=NULL;//共享内存的地址
    struct share_use_st *shm;
    int shmid;//共享内存标识符
    shmid=shmget((key_t)123,sizeof(struct share_use_st),0666|IPC_CREAT);
    if(shmid==-1)
    {
        printf("%s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }
    //将共享内存连接到当前进程的地址空间
    shmbuf=shmat(shmid,0,0);
    if(shmbuf==(void *)-1)
    {
        printf("%s",strerror(errno));
        exit(EXIT_FAILURE);
    }
    printf("\nMemory attach at %x",(int)shm);
    //设置共享内存
    shm=(struct share_use_st*)shmbuf;
    shm->write_flag=0;
    while(run)
    {
        if(shm->write_flag!=0)
        {
            printf("recv:%s\n",shm->value);
            sleep(rand()%3);
            //读取完数据,设置write_flag使共享内存可写
            shm->write_flag=0;
            //输入了end,退出循环
            if(strncmp(shm->value,"end",3)==0)
                run=0;
        }
        else
        {
            sleep(1);
        }
    }
    //把共享内存从当前进程中分离
    if(shmdt(shmbuf)==-1)
    {
        printf("%s",strerror(errno));
        exit(EXIT_FAILURE);
    }
    //删除共享内存
    if(shmctl(shmid,IPC_RMID,0)==-1)
    {
        printf("%s\n",strerror(errno));
        exit(EXIT_FAILURE);
    }
    return EXIT_SUCCESS;
}

  三、使用共享内存的优缺点 

           优点:使用共享内存进行进程间通信非常方便,函数接口也简单,数据的共享还使进程通信时直接访问内存,省去了传送的时间,加快了效率;同时也不想无名管道那样要求通信的进程有一定的父子关系。

            缺点:共享内存没有提供同步的机制,这使得我们在使用共享内存时,往往要借助其他的方式来进行进程间的同步问题。