共享内存允许两个或多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会成为一个进程用户空间内存的一部分,因此这种IPC机制无序内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。与管道或消息队列要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种IPC技术的速度更快。每个进程也存在通过系统调用来执行复制操作的开销
另一方面,共享内存这种IPC机制不由内核控制意味着通常需要通过某些同步方法使得进程不会出现同时访问共享内存的情况(如两个进程同时执行更新操作或者一个进程在从共享内存中获取数据的同时另一个进程正在更新这些数据)。System V信号量天生就是用来完成这种同步的一种方法。当然,还可以使用其他方法,如POSIX信号量和文件锁。

概述

为使用一个共享内存段通常需要执行下面的步骤:

  • 调用shmget创建一个新共享内存段或取得一个既有共享内存段的标识符(即由其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符。
  • 使用shmat来附上共享内存段,即使该段成为调用进程的虚拟内存的一部分。
  • 此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要使用由shmat调用返回的addr值,它是一个指向进程的虚拟地址空间中该共享内存段的起点的指针。
  • 调用shmdt来分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是可选的,并且在进程终止时会自动完成这一步。
  • 调用shmctl来删除共享内存段。只有当当前所有附加内存段的进程都与之分离之后内存段才会被销毁。只有一个进程需要执行这一步。

创建或打开共享内存段

使用共享内存

共享内存在虚拟内存中的位置

在x86-32架构的内存布局中,段被附加在向上增长的堆和向下增长的栈之间未被分配的空间中。通过Linux特有的/proc/PID/maps文件能够看到一个程序映射的共享内存段和共享库的位置。
首先在shell创建两个共享内存段(大小分别为100kb和3200kb),然后启动一个将这两个段附加到由内核选择的地址处的程序。

$ ./svshm_create -p 102400
9633796
$ ./svshm_create -p 3276800
9666565
$ ./svshm_create -p 102400
1015817
$ ./svshm_create -p 3276800
1048586

$ ./svshm_attach 9633796:0 9666565:0
SHMLBA = 4096 (0X1000), PID = 9903
1: 9633796:0 ==> 0xb7f0d000
2: 9666565:0 ==> 0xb7bed000
Sleeping 5 seconds

// 检查/proc/PID/maps文件内容
$ cat

在共享内存中存储指针

共享内存控制操作

共享内存关联数据结构

共享内存的限制

总结