linux 共享内存

共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝。因此,采用共享内存的通信方式效率是非常高的。

 

【应用场景】

 

1. 进程间通讯-生产者消费者模式

 

    生产者进程和消费者进程通讯常使用共享内存,比如一个网络服务器,接入进程收到数据包后,直接写到共享内存中,并唤醒处理进程,处理进程从共享内存中读数据包,进行处理。当然,这里要解决互斥的问题。

 

2. 父子进程间通讯

 

    由于fork产生的子进程和父进程不共享内存区,所以父子进程间的通讯也可以使用共享内存,以POSIX共享内存为例,父进程启动后使用MAP_SHARED建立内存映射,并返回指针ptr。fork结束后,子进程也会有指针ptr的拷贝,并指向同一个文件映射。这样父、子进程便共享了ptr指向的内存区。

 

3. 进程间共享-只读模式

业务经常碰到一种场景,进程需要加载一份配置文件,可能这个文件有100K大,那如果这台机器上多个进程都要加载这份配置文件时,比如有200个进程,那内存开销合计为20M,但如果文件更多或者进程数更多时,这种对内存的消耗就是一种严重的浪费。比较好的解决办法是,由一个进程负责把配置文件加载到共享内存中,然后所有需要这份配置的进程只要使用这个共享内存即可。

 

【共享内存分类】

 

1. POSIX共享内存对象

 

const char shmfile[] = "/tmp";
const int size = 100;

 

shm_open创建一个名称为tmp,大小为100字节的共享内存区对象后,在/dev/shm/下可以看到对应的文件,cat可以看到内容。

root:/home/#ls -al /dev/shm/tmp 
-rw------- 1 root root 100 10-15 13:37 /dev/shm/tmp

 

访问速度:非常快,因为 /dev/shm 是tmpfs的文件系统, 可以看成是直接对内存操作的,速度当然是非常快的。

 

持续性:随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shm_unlink或rm掉文件删除后丢失。

 

2.  POSIX内存映射文件

const char shmfile[] = "./tmp.shm";
const int size = 100;

 

open在指定目录下创建指定名称后文件,cat可以看到内容。

root:/home/#ls -al ./tmp.shm

-rw-------  1 root    root    100 10-15 13:42 tmp.shm

 

访问速度:慢于内存区对象,因为内核为同步或异步更新到文件系统中,而内存区对象是直接操作内存的。

持续性:随文件,即进程重启或内核自举不后丢失,除失显示rm掉文件后丢失。

 

3. SYSTEM V共享内存

 

共享内存创建后,执行ipcs命令,会打印出相应的信息,比如下面所示,key为申请时分配的,可以执行ipcrm -M 0x12345678 删除,nattch字段为1表示有一个进程挂载了该内存。

 

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status     
0x12345678 32769      root      644        10         1

 

访问速度:非常快,可以理解为全内存操作。

持续性: 随内核,即进程重启共享内存中数据不会丢失,内核自举或显示调用shmdt或使用ipcrm删除后丢失。

与POSIX V共享内存区对象不同的是,SYSTEM V的共享内存区对象的大小是在调用shmget创建时固定下来的,而POSIX共享内存区大小可以在任何时刻通过ftruncate修改。

 

 

【代码示例】

 

下面给出三种共享内存使用方法的示例代码,都采用父子进程间通讯,并未考虑互斥,仅做示例供大家参考。

 

1.POSIX共享内存对象

[cpp:nogutter]  view plain copy

  1. /* 
  2.  * Posix shared memory is easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/mman.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15. #include <sys/stat.h>  
  16. #include <fcntl.h>  
  17. #include <errno.h>  
  18.   
  19. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  20.   
  21. const char shmfile[] = "/tmp";  
  22. const int size = 100;  
  23.   
  24. struct namelist   
  25. {  
  26.  int  id;   
  27.  char name[20];  
  28. };  
  29.   
  30. int   
  31. main(void)  
  32. {  
  33.  int fd, pid, status;   
  34.  int *ptr;  
  35.  struct stat stat;  
  36.     
  37.  // create a Posix shared memory  
  38.  int flags = O_RDWR | O_CREAT;  
  39.  fd = shm_open(shmfile, flags, FILE_MODE);  
  40.     if (fd < 0) {  
  41.         printf("shm_open failed, errormsg=%s errno=%d", strerror(errno), errno);  
  42.         return 0;  
  43.     }  
  44.  ftruncate(fd, size);  
  45.  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  46.   
  47.  pid = fork();  
  48.  if (pid == 0) { // child process  
  49.   printf("Child %d: start/n", getpid());  
  50.     
  51.   fd = shm_open(shmfile, flags, FILE_MODE);  
  52.   fstat(fd, &stat);    
  53.   ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  54.   close(fd);  
  55.   struct namelist tmp;  
  56.   
  57.   // store total num in ptr[0];  
  58.   *ptr = 3;  
  59.     
  60.   ptr++;  
  61.   namelist *cur = (namelist *)ptr;  
  62.   
  63.   // store items  
  64.   tmp.id = 1;  
  65.   strcpy(tmp.name, "Nellson");  
  66.   *cur++ = tmp;  
  67.   tmp.id = 2;  
  68.   strcpy(tmp.name, "Daisy");  
  69.   *cur++ = tmp;  
  70.   tmp.id = 3;  
  71.   strcpy(tmp.name, "Robbie");  
  72.   *cur++ = tmp;  
  73.   
  74.   exit(0);  
  75.  } else// parent process  
  76.   sleep(1);  
  77.   struct namelist tmp;  
  78.   
  79.   int total = *ptr;  
  80.   printf("/nThere is %d item in the shm/n", total);   
  81.     
  82.   ptr++;  
  83.   namelist *cur = (namelist *)ptr;  
  84.   
  85.   for (int i = 0; i< total; i++) {  
  86.    tmp = *cur;  
  87.    printf("%d: %s/n", tmp.id, tmp.name);  
  88.    cur++;  
  89.   }  
  90.   
  91.   printf("/n");  
  92.   waitpid(pid, &status, 0);  
  93.  }  
  94.   
  95.  // remvoe a Posix shared memory from system  
  96.  printf("Parent %d get child status:%d/n", getpid(), status);  
  97.  return 0;  
  98. }  
  

 

 

编译执行

root:/home/ftpuser/ipc#g++ -o shm_posix -lrt shm_posix.cc      
root:/home/ftpuser/ipc#./shm_posix 
Child 2280: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2279 get child status:0

 

2.POSIX文件映射

 

[cpp:nogutter]  view plain copy

  1. /* 
  2.  * Posix shared memory is easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/mman.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15. #include <sys/stat.h>  
  16. #include <fcntl.h>  
  17.   
  18. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  19.   
  20. const char shmfile[] = "./tmp.shm";  
  21. const int size = 100;  
  22.   
  23. struct namelist   
  24. {  
  25.  int  id;   
  26.  char name[20];  
  27. };  
  28.   
  29. int main(void)  
  30. {  
  31.  int fd, pid, status;   
  32.  int *ptr;  
  33.  struct stat stat;  
  34.     
  35.  // create a Posix shared memory  
  36.  int flags = O_RDWR | O_CREAT;  
  37.  fd = open(shmfile, flags, FILE_MODE);  
  38.  ftruncate(fd, size);  
  39.  ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  40.   
  41.  pid = fork();  
  42.  if (pid == 0) { // child process  
  43.   printf("Child %d: start/n", getpid());  
  44.     
  45.   fd = open(shmfile, flags, FILE_MODE);  
  46.   fstat(fd, &stat);    
  47.   ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);   
  48.   close(fd);  
  49.   struct namelist tmp;  
  50.   
  51.   // store total num in ptr[0];  
  52.   *ptr = 3;  
  53.     
  54.   ptr++;  
  55.   namelist *cur = (namelist *)ptr;  
  56.   
  57.   // store items  
  58.   tmp.id = 1;  
  59.   strcpy(tmp.name, "Nellson");  
  60.   *cur++ = tmp;  
  61.   tmp.id = 2;  
  62.   strcpy(tmp.name, "Daisy");  
  63.   *cur++ = tmp;  
  64.   tmp.id = 3;  
  65.   strcpy(tmp.name, "Robbie");  
  66.   *cur++ = tmp;  
  67.   
  68.   exit(0);  
  69.  } else// parent process  
  70.   sleep(1);  
  71.   struct namelist tmp;  
  72.   
  73.   int total = *ptr;  
  74.   printf("/nThere is %d item in the shm/n", total);   
  75.     
  76.   ptr++;  
  77.   namelist *cur = (namelist *)ptr;  
  78.   
  79.   for (int i = 0; i< total; i++) {  
  80.    tmp = *cur;  
  81.    printf("%d: %s/n", tmp.id, tmp.name);  
  82.    cur++;  
  83.   }  
  84.     
  85.   printf("/n");  
  86.   waitpid(pid, &status, 0);  
  87.  }  
  88.  printf("Parent %d get child status:%d/n", getpid(), status);  
  89.  return 0;  
  90. }  

 

编译执行

 

root:/home/ftpuser/ipc#g++ -o map_posix map_posix.cc 
root:/home/ftpuser/ipc#./map_posix 
Child 2300: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2299 get child status:0

 

3.SYSTEM V 共享内存对象

 

[cpp:nogutter]  view plain copy

  1. /* 
  2.  * System V shared memory in easy to use in Linux 2.6, in this program, we  
  3.  * shared a memory between parent process and child process, stored several 
  4.  * objects of struct namelist in it. We store number of items in ptr[0]. 
  5.  */  
  6.   
  7. #include <unistd.h>  
  8. #include <stdio.h>  
  9. #include <stdlib.h>  
  10. #include <string.h>  
  11. #include <sys/ipc.h>  
  12. #include <sys/shm.h>  
  13. #include <sys/types.h>  
  14. #include <sys/wait.h>  
  15.   
  16. #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
  17. const char shmfile[] = "./tmp.shm";  
  18. const int shmsize = 10;  
  19.   
  20. struct namelist   
  21. {  
  22.  int  id;   
  23.  char name[20];  
  24. };  
  25.   
  26. int   
  27. main(void)  
  28. {  
  29.  int shmid, pid, status;   
  30.  int *ptr;  
  31.  struct shmid_ds buff;  
  32.   
  33.  // create a systym V shared memory  
  34.  //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
  35.  shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  
  36.   
  37.  pid = fork();  
  38.  if (pid == 0) { // child process  
  39.   printf("Child %d: start/n", getpid());  
  40.   //shmid = shmget(ftok(shmfile, 0), shmsize, SVSHM_MODE | IPC_CREAT);  
  41.   shmid = shmget((key_t)0x12345678, shmsize, SVSHM_MODE | IPC_CREAT);  
  42.   ptr = (int *) shmat(shmid, NULL, 0);  
  43.   shmctl(shmid, IPC_STAT, &buff);  
  44.   
  45.   struct namelist tmp;  
  46.   
  47.   // store total num in ptr[0];  
  48.   *ptr = 3;  
  49.     
  50.   ptr++;  
  51.   namelist *cur = (namelist *)ptr;  
  52.   
  53.   // store items  
  54.   tmp.id = 1;  
  55.   strcpy(tmp.name, "Nellson");  
  56.   *cur++ = tmp;  
  57.   tmp.id = 2;  
  58.   strcpy(tmp.name, "Daisy");  
  59.   *cur++ = tmp;  
  60.   tmp.id = 3;  
  61.   strcpy(tmp.name, "Robbie");  
  62.   *cur++ = tmp;  
  63.     
  64.   exit(0);  
  65.  } else// parent process  
  66.   sleep(1);  
  67.   shmctl(shmid, IPC_STAT, &buff);  
  68.   ptr = (int *) shmat(shmid, NULL, 0);   
  69.   struct namelist tmp;  
  70.   
  71.   int total = *ptr;  
  72.   printf("/nThere is %d item in the shm/n", total);   
  73.     
  74.   ptr++;  
  75.   namelist *cur = (namelist *)ptr;  
  76.   
  77.   for (int i = 0; i< total; i++) {  
  78.    tmp = *cur;  
  79.    printf("%d: %s/n", tmp.id, tmp.name);  
  80.    cur++;  
  81.   }  
  82.   
  83.   printf("/n");  
  84.   waitpid(pid, &status, 0);  
  85.  }  
  86.   
  87.  // remvoe a systym V shared memory from system  
  88.  shmctl(shmid, IPC_RMID, NULL);  
  89.  printf("Parent %d get child status:%d/n", getpid(), status);  
  90.  return 0;  
  91. }  
  92.   
  93.    

 

编译执行

 

root:/home/ftpuser/ipc#g++ -o shm_v shm_v.cc  
root:/home/ftpuser/ipc#./shm_v 
Child 2323: start

There is 3 item in the shm
1: Nellson
2: Daisy
3: Robbie

Parent 2322 get child status:0


【性能测试】

下面对三种方式进行性能测试,比较下差异。

测试机信息:

AMD Athlon(tm) Neo X2 Dual Core Processor 6850e

cpu:1.7G

os: Linux 2.6.18

 

测试方式:

打开大小为SIZE的共享内存,映射到一个int型的数组中,循环写数组、读数组。

重复10W次,计算时间开销。

 

内存大小

Shmopen+mmap(ms)

Open+mmap

Shmget

4k

1504

1470

1507

16k

6616

6201

5994

64k

25905

24391

24315

256k

87487

76981

69417

1M

253209

263431

241886

 

重复1K次,计算时间开销。

 

内存大小

Shmopen+mmap(ms)

Open+mmap(ms)

Shmget(ms)

1M

5458

5447

5404

4M

21492

21447

21307

16M

90880

93685

87594

32M

178000

214900

193000

 

分析:

Sytem V方式读写速度快于POSIX方式,而POSIX 共享内存和文件映射方式相差不大, 共享内存性能略优。

 

附上测试源码:

[cpp:nogutter]  view plain copy

  1. /* 
  2.  * 共享内存读写速度测试 
  3.  */  
  4. #include <unistd.h>  
  5. #include <stdio.h>  
  6. #include <stdlib.h>  
  7. #include <string.h>  
  8. #include <sys/ipc.h>  
  9. #include <sys/mman.h>  
  10. #include <sys/types.h>  
  11. #include <sys/wait.h>  
  12. #include <sys/stat.h>  
  13. #include <fcntl.h>  
  14. #include <unistd.h>  
  15. #include <stdio.h>  
  16. #include <stdlib.h>  
  17. #include <string.h>  
  18. #include <sys/ipc.h>  
  19. #include <sys/mman.h>  
  20. #include <sys/types.h>  
  21. #include <sys/wait.h>  
  22. #include <sys/stat.h>  
  23. #include <fcntl.h>  
  24. #include <errno.h>  
  25. #include <unistd.h>  
  26. #include <stdio.h>  
  27. #include <stdlib.h>  
  28. #include <string.h>  
  29. #include <sys/ipc.h>  
  30. #include <sys/shm.h>  
  31. #include <sys/types.h>  
  32. #include <sys/wait.h>  
  33. #include "module_call.h"  
  34.   
  35. #define FILE_MODE (S_IRUSR | S_IWUSR)  
  36. #define SVSHM_MODE (SHM_R | SHM_W | SHM_R>>3 | SHM_R>>6)  
  37.   
  38. enum emType  
  39. {  
  40.     SHMOPEN = 0x01,  
  41.     OPEN = 0x02,  
  42.     SHMGET = 0x04,  
  43. };  
  44.   
  45. void * GetShmMem(emType type, int size)  
  46. {  
  47.     void * ptr = NULL;  
  48.     switch (type)  
  49.     {  
  50.         case SHMOPEN:  
  51.             {  
  52.                 const char shmfile[] = "/tmp";  
  53.                 int flags = O_RDWR | O_CREAT;  
  54.                 int fd = shm_open(shmfile, flags, FILE_MODE);  
  55.                 if (fd < 0)  
  56.                 {  
  57.                     printf("shm_open failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  58.                     return NULL;  
  59.                 }  
  60.                   
  61.                 ftruncate(fd, size);  
  62.   
  63.                 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);     
  64.                 if (MAP_FAILED == ptr)  
  65.                 {  
  66.                     printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  67.                     return NULL;  
  68.                 }  
  69.   
  70.                 break;  
  71.             }  
  72.         case OPEN:  
  73.             {  
  74.                 const char shmfile[] = "./tmp.shm";  
  75.                 int flags = O_RDWR | O_CREAT;  
  76.                 int fd = open(shmfile, flags, FILE_MODE);  
  77.                 if (fd < 0)  
  78.                 {  
  79.                     printf("ope failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  80.                     return NULL;  
  81.                 }  
  82.   
  83.                 ftruncate(fd, size);  
  84.                   
  85.                 ptr = (int *)mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);                 
  86.                 if (MAP_FAILED == ptr)  
  87.                 {  
  88.                     printf("mmap failed, errormsg=%s errno=%d/n", strerror(errno), errno);    
  89.                     return NULL;  
  90.                 }  
  91.   
  92.                 break;  
  93.             }  
  94.   
  95.         case SHMGET:  
  96.             {  
  97.                 int shmid;    
  98.                 struct shmid_ds buff;  
  99.                 const char shmfile[] = "./tmp.shm_v";  
  100.                 shmid = shmget(ftok(shmfile, 0), size, SVSHM_MODE | IPC_CREAT);  
  101.                 if (shmid < 0)  
  102.                 {  
  103.                     printf("shmget failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  104.                     return NULL;  
  105.                 }  
  106.                   
  107.                 ptr = (int *) shmat(shmid, NULL, 0);  
  108.                 if ((void *) -1 == ptr)  
  109.                 {  
  110.                     printf("shmat failed, errormsg=%s errno=%d/n", strerror(errno), errno);  
  111.                     return NULL;  
  112.                 }  
  113.                   
  114.                 shmctl(shmid, IPC_STAT, &buff);  
  115.                 break;  
  116.             }  
  117.     }  
  118.   
  119.     return ptr;  
  120. }  
  121.   
  122. int realmain(int size, int loop, emType type)  
  123. {  
  124.     int * array_int = NULL;  
  125.   
  126.     /* get shmmem*/  
  127.     array_int = (int *)GetShmMem(type, size);  
  128.     if (NULL == array_int)  
  129.     {  
  130.         printf("GetShmMem failed/n");  
  131.         return -1;  
  132.     }  
  133.   
  134.     /* loop */  
  135.     int array_num = size/sizeof(int);  
  136.     modulecall::call_start();  
  137.     while (0 != loop)  
  138.     {  
  139.         /* write */  
  140.         for (int i = 0; i < array_num; i++)  
  141.         {  
  142.             array_int[i] = i;  
  143.         }  
  144.   
  145.         /* read */  
  146.         for (int i = 0; i < array_num; i++)  
  147.         {  
  148.             if (array_int[i] != i)  
  149.             {  
  150.                 printf("ShmMem is invalid i=%d v=%d/n", i, array_int[i]);  
  151.                 return -1;  
  152.             }  
  153.         }  
  154.       
  155.         loop--;  
  156.     }  
  157.     modulecall::call_end();  
  158.     printf("timecost=%lld/n", modulecall::call_timecost());  
  159.   
  160.     return 0;  
  161. }  
  162.   
  163. int main(int argc, char ** argv)  
  164. {  
  165.     if (argc < 4)  
  166.     {  
  167.         printf("usage: %s size loop shmtype(1-shmposix 2-mapposix 4-shmv 7-all)/n", argv[0]);  
  168.         return -1;  
  169.     }  
  170.   
  171.     const int size = atoi(argv[1]);  
  172.     int loop = atoi(argv[2]);  
  173.     const int type = atoi(argv[3]);  
  174.   
  175.     if ((type&SHMOPEN) == SHMOPEN)  
  176.     {  
  177.         printf("shmopen ");  
  178.         realmain(size, loop, SHMOPEN);  
  179.     }  
  180.   
  181.     if ((type&OPEN) == OPEN)  
  182.     {  
  183.         printf("open    ");  
  184.         realmain(size, loop, OPEN);  
  185.     }  
  186.   
  187.     if ((type&SHMGET) == SHMGET)  
  188.     {  
  189.         printf("shmget  ");  
  190.         realmain(size, loop, SHMGET);  
  191.     }     
  192.   
  193.     return 0;  
  194. }  



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值