初窥linux共享内存

共享内存的linux IPC的一个方式。最简单的解释就是同一段物理内存被映射到不同进程的地址空间。任和一个被映射的进程对该内存的写操作对其他被映射是可见的。
可以在命令查看当前系统共享内存的信息。


下面我们有4个文件,分别是公共文件,shm_write(创建共享内存) shm_read(读取共享内存内容) shm_clean(清除共享内存内容) common.h一个公共文件

common.h

  1 #ifndef _COMMON_H
  2 #define _COMMON_H
  3 
  4 #include <sys/types.h>
  5 #include <sys/stat.h>
  6 #include <sys/shm.h>
  7 #include <stdlib.h>
  8 
  9 #define SHM_KEY         0x00000010
 10 #define BUFFSIZE        0x00001000
 11 
 12 #define SHM_PERMS (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
 13 
 14 struct shm_mm
 15 {
 16         int num;
 17         char buff[BUFFSIZE];
 18 };
 19 
 20 #endif

shm_write.c

  1 #include "common.h"
  2 #include <stdio.h>
  3 
  4 int main()
  5 {
  6         int shmid;
  7         shmid = shmget(SHM_KEY, sizeof(struct shm_mm), IPC_EXCL|IPC_CREAT|SHM_PERMS);
  8         if(shmid == -1)
  9         {
 10                 printf("shmget failed\n");
 11                 exit(1);
 12         }
 13 
 14         struct shm_mm *pshm = shmat(shmid, NULL, 0);
 15         if(pshm == (void*)-1)
 16         {
 17                 printf("shmat failed\n");
 18                 exit(2);
 19         }
 20 
 21         pshm->num = 100;
 22         for(int i = 0; i < 10 && i < BUFFSIZE; i++)
 23         {
 24                 pshm->buff[i] = 'a';
 25         }
 26 
 27 }

shm_read.c

  1 #include "common.h"
  2 #include <stdio.h>
  3 
  4 int main()
  5 {
  6         int shmid;
  7         shmid = shmget(SHM_KEY,0, 0);
  8         if(shmid == -1)
  9         {
 10                 printf("shmget failed\n");
 11                 exit(1);
 12         }
 13 
 14         struct shm_mm *pshm = shmat(shmid, NULL, 0);
 15         if(pshm == (void*)-1)
 16         {
 17                 printf("shmat failed\n");
 18                 exit(2);
 19         }
 20 
 21         printf("num=%d\n", pshm->num);
 22         for(int i = 0; i < 10 && i < BUFFSIZE; i++)
 23         {
 24                 printf("%c\t", pshm->buff[i]);
 25         }
 26          printf("\n");
 27 
 28         getchar();
 29         return 0;
 30 }

shm_clean.c

  1 #include "common.h"
  2 #include <stdio.h>
  3 
  4 int main()
  5 {
  6         int shmid = shmget(SHM_KEY, 0, 0);
  7         if(-1 == shmid)
  8         {
  9                 printf("shmget failed\n");
 10                 exit(0);
 11         }
 12         int ret = shmctl(shmid, IPC_RMID, 0);
 13         if(-1 == ret)
 14         {
 15                 printf("shmctl failed\n");
 16                 exit(1);
 17         }
 18         return 0;
 19 }

![read_out.png](http://upload-images.jianshu.io/upload_images/541012-8ff9b7b6ec4b5676.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

我们运行shm_write进程
查看当前系统的共享内存

2016-10-10 23-52-41屏幕截图.png

我们可以看到最后一行, 第一列的就是我们代码里面的SHM_KEY 权限也是我们自己设定的 SHM_PERMS, 大小是sizeof(shm_mm)。 因为shm_write进程以退出,所以现在attach在该内存的进程数量为0。
我们启动shm_read

read_out.png

read进程读到了write写进去的数据.
现在我们可以启动clean进程清除刚才创建的贡献内存。也可以用命令 ipcrm -m shmid 清除掉对于的共享内存。

通过上述实现,我们看到,当进程退出后,内核自动会把该进程和共享内存的映射给清掉,就是我们通过ipcs -m 看到的attach的子段。如果我们想在进程不退出就把这个映射这清掉。通过调用shmdt即可。

和其他共享数据一下,当多个进程对同一个共享内存进行读写的时候,仍然会有竞争。所以我们需要一些如信号量,进程锁等高级的进程同步控制原语来进行同步操作。
当然,共享内存还受到一些参数的影响.
/proc/sys/kernel/shmall:限制系统用在共享内存上的内存总页数。注意是页数,单位为4k。
/proc/sys/kernel/shmmax:限制一个共享内存段的最大长度,字节为单位。
/proc/sys/kernel/shmmni:限制整个系统可创建的最大的共享内存段个数。

推荐阅读更多精彩内容