这里介绍Linux下常见的共享内存技术。

1、 Linux XSIIPC 下的共享内存机制

Linux XSIIPC 机制有三种:消息队列,信号量,共享内存。使用这三种方式通信需要特殊的接口,因为这三种通信方式不是使用普通文件系统的标示符来标示的,它们有自己的标识方式和处理机制。这里主要给出其中使用共享内存的方式,其它的机制使用方法都很类似。

(1)标识关键字

和文件标识类似,共享内存对象通过其 IPC 标识来引用到,而 IPC 标识需要通过关键字来创建和获取。

创建的方法大致如下几种:

  1. 服务端通过 IPC_PRIVATE 的关键字来创建一个新的 IPC 结构,并将返回的标识存储到一个地方(例如文件系统中)让客户能够获取到,之后通过标识符进行双方通信。缺点是需要借助文件系统传递标识符。

  2. 客户和服务商定好一个定义的关键字存于头文件中,服务端先通过指定这个关键字创建一个新的 IPC 结构并返回标识符,之后客户端和服务端通过这个关键字就可以得到创建好的标示符相互通信。缺点是,有可能这个关键字已经和某个其他的 IPC 结构关联了

  3. 为克服2)的缺点,确保关键字的唯一性,引入了 ftok 函数。客户和服务进程可以商定好一个路径和 project ID (这个 project ID 是一个介于0到255之间的字符),然后调用函数 ftok 将这两个值转换成一个关键字。

(2)共享内存的使用

常用操作:

  • 使用 shmget() ,获得并返回由关键字指定的共享内存区域标识,如果不存在共享内存区域则创建之,共享内存区域的数据结构使用 shmid_ds 进行表示。
  • 使用 shmat() ,将共享内存区域映射到进程地址空间,并返回相应的进程内存空间地址(同时会使得 shmid_ds 中相应的引用计数成员增加),返回的地址可以像正常指针一样使用,用于向共享内存中添加和获取进程通信数据。
  • 使用 shmdt() ,解除某个进程地址与相应共享内存区域之间的映射(会使得 shmid_ds 中相应的引用计数成员减少)。
  • 使用 shmctl() ,在结束时释放共享内存区域资源。通过该函数可以设置共享内存的各个参数成员,以及初始化,获取,删除(只对标识进行删除,但是内存在没有使用的进程之后才删除)等,以便管理共享内存区域。

(3) XSI 共享内存区域的地址范围

系统自行分配的共享内存区域所处地址对系统的依赖性很大。实际内存分配情况,应参照实际系统来确定。
下面是基于 intel 的一种 linux 下的共享内存所处系统内存的位置:

            +----------------------+ \
high address|                      |  \Command Line arguments
            |                      |  /And environment variables.
            +----------------------+ /
            |    Stack             |<-----0xbffff9e4
            |                      |
            |                      |
            +----------------------+
            |   Shared Memory      |<----0x4017a6a0 \ Shared memory of
            |                      |<----0x40162000 / 100,000 bytes.
            +----------------------+
            |                      |
            |                      |<----0x0806c368 \ Malloc of
            |    Heap              |<----0x08053cc8 / 100,000 bytes.
            +----------------------+
            | uninitialized data   |<----0x08053cc0 \ array[] of
            |    (bss)             |<----0x0804a080 / 40,000 bytes.
            +----------------------+
            |                      |
            |   initialized data   |
            +----------------------+
            |                      |
            |        text          |
low address +----------------------+

(4)系统对共享内存的限制

共享内存区域大小受限于系统,大小限制包括:一个共享内存段的最大字节数,一个共享内存段的最小字节数,系统***享内存段的最大数目,每个进程共享内存段最大数目。

查看系统共享内存限制有如下方法:

通过 ipcs 命令查看系统共享内存限制

miracle.lv@cnszs2508:~/study/codes/shareMemory$ ipcs -lm
------ Shared Memory Limits --------
max number of segments = 4096
max seg size (kbytes) = 32768
max total shared memory (kbytes) = 8388608
min seg size (bytes) = 1

另外还可通过查看 proc 文件

共享内存段最大个数

$cat /proc/sys/kernel/shmmni
4096

单个共享内存段最大字节数

$cat /proc/sys/kernel/shmmax
33554432

系统***享内存页总数

$cat /proc/sys/kernel/shmall
2097152

设置共享内存限制方法

修改共享内存段最大个数

#sysctl -w kernel.shmmni=4096
#echo “kernel.shmmni=4096″ >> /etc/sysctl.conf

修改单个共享内存段最大字节数

#sysctl -w kernel.shmmax=1073741824
#echo “kernel.shmmax=1073741824″ >> /etc/sysctl.conf

修改系统***享内存页总数

#sysctl -w kernel.shmall=2097152
#echo “kernel.shmall=2097152″ >> /etc/sysctl.conf

(5)在系统中管理XSI的共享内存资源

XSI 的共享内存,不像 mmap 映射的共享内存那样会最终会将内容写入到实际的文件中。它的共享区域其实是挂接在 shm 类型的文件系统上的,而 shm 文件系统存于交换分区,断电会消失。进程运行时如果没有被显示释放,那么即使进程退出也保存在系统中,不会被删除,通过 ipc 提供的相关系统工具,可以查看和管理相应的资源。

列出当前系统中 XSIIPC 资源

miracle.lv@cnszs2508:~/study/codes/shareMemory$ ipcs

输入之后,显示如下:

------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status
0x01050406 327682     miracle.lv 600        127        0

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0x002fa327 262152     root       666        2

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

删除 id327682 的共享内存资源

miracle.lv@cnszs2508:~/study/codes/shareMemory$ ipcrm -m 327682

输入之后,查看当前资源会发现原有 id327682 的共享内存资源消失了:

miracle.lv@cnszs2508:~/study/codes/shareMemory$ ipcs
------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0x002fa327 262152     root       666        2

------ Message Queues --------
key        msqid      owner      perms      used-bytes   messages

(6)实例代码

参考: sharedMemoryXSI.tgz

2、使用 mmap 实现的共享内存

MMap 并不是为共享内存设计的,但是使用 MMap 实现共享内存也是其重要的功能之一。

(1)使用 mmap 实现共享内存的方式和特点

(a) mmap 映射普通文件实现共享内存

特点:

  • 可在任何不同进程之间实现共享内存通信
  • 需要一个普通文件进行 mmap 映射,来完成创建共享内存(映射内存)的操作。
  • 修改映射内存并不实时修改物理文件,最后使用 munmap 取消映射时,会将内存内容写入文件。
  • 虽然可以修改文件但是不能够修改文件的大小。
  • 用户进程通信的有效映射空间不超过文件大小+一个页面大小的和。

mmap 映射普通文件以页为单位,文件的大小做为参考界限。映射之后,可用的映射内存大小不能超过“文件大小对应页大小整数倍部分大小+一个页大小”(如页大小为 4096byte ,文件大小为 4097byte ,映射大小为 4096+4096 ,按页取整),写入时,最后 4095byte 内容不会写入文件。如果需要的映射内存较大,我们可以在 mmap 之前通过 lseek+write 的方法,根据需要把文件大小变大。

(b) mmap 映射特殊设备文件( /dev/zero )映射或匿名映射实现共享内存

特点:

  • 使用 /dev/zero 映射的内存,自动被初始化为0。
  • 使用匿名映射方式不需要借助文件进行映射,需要借助 mmapMAP_ANONYMOUS 标志。
  • 不限制映射内存的大小,但只能在只能在有亲子关系的进程之间实现通信。

另外,使用 mmap 映射之后,可以将文件关闭。由于映射内存是进程相关的,所以如果不调用 munmap ,进程退出时相应的映射也会自动消失(相当自动调用 munmap ),所以有的进程并没有调用过 munmap 。想要使用共享内存的特性,需要借助 MAP_SHARED 标记。

更多介绍参考:http://blog.sina.com.cn/s/blog_6a1837e90100o9vl.html

(2)实例代码

参考:sharedMemoryMMap.tgz

3、其它

待更新

coding 需要确定的问题:

  1. getshm 的时候,如果已经存在一个关键字的时候,是否在创建关键字的时候返回错误?
  2. getshm 的时候,如果 get 没有创建的共享内存,并且没有 create 标志,那么返回的是错误还是尝试建立一个共享内存区域?
  3. keyid 是负数可以吗?