标题:共享内存


进程之间进行通信,有共享内存,管道,消息队列,事件和信号量等。其中,共享内存是速度和性能最好的。

 

Windows中,单个计算机上共享数据的底层机制是内存映射文件。如果想要达到较高的性能和较小的开销,内存映射文件将是最佳的实现机制。内存映射文件是通过两个或多个进程映射同一个文件映射对象的视图来实现的,这意味着它们将共享物理存储器的同一个页面。因此,当一个进程将数据写入一个共享文件映射对象的视图时,其他进程可以立即看到它们视图中的数据变更情况。如果多个进程共享单个文件映射对象,那么所有进程必须使用相同的名字来表示该文件映射对象。

 

首先来看共享内存的数据发送。在数据发送进程中,程序先调用CreateFileMapping()函数创建一个命名的内存映射对象,得到相应内存起始位置指针lhShareMemory。如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,得到指向映射到内存起始位置指针lpBuffer并通过该指针读写共享的内存区域。最后使用UnmapViewOfFile()函数来解除视图映射,传入参数为lpBuffer,具体代码如下:

 

  HANDLE lhShareMemory;

  char *lpBuffer = NULL;

  //创建一个有名的共享内存

  lhShareMemory = CreateFileMapping(

  HANDLE(0xFFFFFFFF), //0xFFFFFFFF表示创建一个进程间共享的对象

  NULL,

  PAGE_READWRITE,    //读写共享

  0,

  100,  //共享区间大小

"mySharedMemory"); //映射文件名,即共享内存的名称,一个进程创建,

                     //另外进程可根据该名字打开

  if (NULL == lhShareMemory)

  {

  if (ERROR_ALREADY_EXISTS == GetLastError())

  {

        printf("file already exists\n");

  }

  else

  {

        printf("Create sheared memory unsuccessfully!\n");

  }

        return;

  }

  //映射到本进程的地址空间

  lpBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_WRITE, 0, 0,100);

  if (NULL == lpBuffer)

  {

        printf("Get share memory unsuccessfully!\n");

        return;

  }

  strcpy(lpBuffer,  (char*)(LPCTSTR)m_strsend);

  UnmapViewOfFile(lpBuffer); //取消本进程地址空间的映射

      lpBuffer = NULL;

 

接下来是数据接收。在数据接收进程中,首先调用OpenFileMapping()函数打开一个命名的内存映射文件对象,得到相应内存起始位置指针lhShareMemory。如果打开成功,则调用MapViewOfFile()函数映射对象的一个视图,得到指向映射到内存的第一个字节的指针 lpcBuffer并通过该指针读写共享的内存区域。最后调用UnmapViewOfFile()函数来解除视图映射,传入参数为lpcBuffer,调用CloseHandle()函数来关闭内存映射文件,传入参数为lhShareMemory,具体代码如下:

 

  HANDLE hShareMem;

      char* lpcBuffer;

       //获得共享内存句柄,通过共享名字”mySharedMemory”来打开

  lhShareMemory = OpenFileMapping(FILE_MAP_READ, false, "mySharedMemory");

  if (NULL == hShareMem)

  {

        printf("Open share memory unsuccessfully!\n");

        return;

  }

  lpcBuffer = (char*)MapViewOfFile(lhShareMemory, FILE_MAP_READ, 0, 0, 100);

  if (NULL == lpcBuffer)

  {

        printf"Open share memory unsuccessfully!\n");

        return;

  }

  m_strrecieve = lpcBuffer;

  UnmapViewOfFile(lpcBuffer) //取消本进程地址空间的映射;

  lpcBuffer = NULL;

  CloseHandlelhShareMemory

 

Linux共享内存方式:

 

Mmap

 

/*-------------map_normalfile1.c-----------*/

#include <sys/mman.h>

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

typedef struct{

  char name[4];

  int  age;

}people;

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);

  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,2 );

    ( *(p_map+i) ).age = 20+i;

  }

  printf(" initialize over \n ")

  sleep(10);

  munmap( p_map, sizeof(people)*10 );

  printf( "umap ok \n" );

}

/*-------------map_normalfile2.c-----------*/

#include <sys/mman.h>

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

typedef struct{

  char name[4];

  int  age;

}people;

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 );

}

 

#include <sys/mman.h>

#include <sys/types.h>

#include <fcntl.h>

#include <unistd.h>

typedef struct{

  char name[4];

  int  age;

}people;

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();

  }

  temp = 'a';

  for(i = 0;i<5;i++)

  {

    temp += 1;

    memcpy((*(p_map+i)).name, &temp,2);

    (*(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" );

}

posix共享内存

shmget

#include <sys/shm.h>

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

     成功返回共享存储ID,错误返回-1

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

     cmd IPC_STAT, IPC_SET, IPC_RMID, SHM_LOCK, SHM_UNLOCK

 

连接到地址空间

void *shmat (int shmid ,const void *addr, int flag);

 

对共享内存操作结束时,脱离该段

int shmdt (void *addr);

 

 

要运行程序,需要在当前目录下建立一个share文件,share是一个空文件,没有任何意义,只是函数ftok需要一个文件名作参数,ftok另一个参数可以为任何数字。

 

程序运行后,分为父子进程,子进程申请共享内存,然后等待父进程继续执行,父进程首先等待子进程申请到共享内存标识,然后输出共享内存中的内容,为了演示共享内存可以随时更新,程序中在子进程里产生随机数写入共享内存供父进程读取。

代码如下:

 

#include<stdio.h>

#include<unistd.h>

#include<stdlib.h>

#include<string.h>

 

#include<time.h>

#include<signal.h>

#include<sys/ipc.h>

#include<sys/shm.h>

#include<sys/types.h>

 

int shmID;

char * buf;

 

void finalize(int signo)

{

 shmctl(shmID,IPC_RMID,NULL);

 

 exit(0);

}

 

int main()

{

 int i = 1; 

 key_t shmKey;

 

 signal(SIGINT,finalize);

 signal(SIGTERM,finalize);

 

 if(fork() == 0) //子进程

 { 

  shmKey = ftok("share",16);  //可以使用任何大于0的数字,如果名字和数相同,则产生的key相同。

  if(shmKey == -1)

  {

   printf("创建key出错\n");

   exit(-1);

  }

 

  shmID = shmget(shmKey,20,IPC_CREAT | IPC_EXCL | 0666);

  if(shmID == -1)

  {

   printf("创建共享标识出错\n");

   exit(-1);

  }

 

  sleep(2); //等待父进程执行,好显示第一行为空。

  while(1)

  {

   buf = (char *)shmat(shmID,NULL,0);

   srandom(time(NULL));

   sprintf(buf,"%d",random());

   shmdt(buf); 

  }

 }

 else  //父进程

 {

  sleep(1); //让子进程先执行,以建立内存映射。

 

  shmKey = ftok("share",16);  //可以使用任何大于0的数字,如果名字和数相同,则产生的key相同。

  if(shmKey == -1)

  {

   printf("创建key出错\n");

   exit(-1);

  }

 

  shmID = shmget(shmKey,20,0); //0表示如果shmKey映射的不存在则报错。

  if(shmID == -1)

  {

   printf("引用共享标识出错\n");

   exit(-1);

  }

 

  while(1)

  {

   buf = (char *)shmat(shmID,NULL,0);

   printf("%d. 现在共享内存中的内容为:%s\n",i++,buf);

   shmdt(buf);

   sleep(1);

  }

 }

 

 return 0;

}

System V共享内存就是系统V共享内存从来不把数据真正写到磁盘文件中去,而mmap机制在munmap的时候将数据写到磁盘文件。系统V共享内存机制实际是通过shm文件系统中的文件实现的,shm文件系统的安装点在交换分区上,系统重新引导后,所有的内容都丢失。



看文字不过瘾?点击我,进入腾讯课堂视频教学
麦洛科菲长期致力于IT安全技术的推广与普及,我们更专业!我们的学员已经广泛就职于BAT360等各大IT互联网公司。详情请参考我们的 业界反馈 《周哥教IT.C语言深学活用》视频

我们的微信公众号,敬请关注