这里介绍Linux下常见的共享内存技术。
1、 Linux XSI
的 IPC
下的共享内存机制
Linux XSI
的 IPC
机制有三种:消息队列,信号量,共享内存。使用这三种方式通信需要特殊的接口,因为这三种通信方式不是使用普通文件系统的标示符来标示的,它们有自己的标识方式和处理机制。这里主要给出其中使用共享内存的方式,其它的机制使用方法都很类似。
(1)标识关键字
和文件标识类似,共享内存对象通过其 IPC
标识来引用到,而 IPC
标识需要通过关键字来创建和获取。
创建的方法大致如下几种:
服务端通过
IPC_PRIVATE
的关键字来创建一个新的IPC
结构,并将返回的标识存储到一个地方(例如文件系统中)让客户能够获取到,之后通过标识符进行双方通信。缺点是需要借助文件系统传递标识符。客户和服务商定好一个定义的关键字存于头文件中,服务端先通过指定这个关键字创建一个新的
IPC
结构并返回标识符,之后客户端和服务端通过这个关键字就可以得到创建好的标示符相互通信。缺点是,有可能这个关键字已经和某个其他的IPC
结构关联了为克服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
提供的相关系统工具,可以查看和管理相应的资源。
列出当前系统中 XSI
的 IPC
资源
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
删除 id
为 327682
的共享内存资源
miracle.lv@cnszs2508:~/study/codes/shareMemory$ ipcrm -m 327682
输入之后,查看当前资源会发现原有 id
为 327682
的共享内存资源消失了:
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)实例代码
2、使用 mmap
实现的共享内存
MMap
并不是为共享内存设计的,但是使用 MMap
实现共享内存也是其重要的功能之一。
(1)使用 mmap
实现共享内存的方式和特点
(a) mmap
映射普通文件实现共享内存
特点:
- 可在任何不同进程之间实现共享内存通信
- 需要一个普通文件进行
mmap
映射,来完成创建共享内存(映射内存)的操作。 - 修改映射内存并不实时修改物理文件,最后使用
munmap
取消映射时,会将内存内容写入文件。 - 虽然可以修改文件但是不能够修改文件的大小。
- 用户进程通信的有效映射空间不超过文件大小+一个页面大小的和。
mmap
映射普通文件以页为单位,文件的大小做为参考界限。映射之后,可用的映射内存大小不能超过“文件大小对应页大小整数倍部分大小+一个页大小”(如页大小为 4096byte
,文件大小为 4097byte
,映射大小为 4096+4096
,按页取整),写入时,最后 4095
个 byte
内容不会写入文件。如果需要的映射内存较大,我们可以在 mmap
之前通过 lseek+write
的方法,根据需要把文件大小变大。
(b) mmap
映射特殊设备文件( /dev/zero
)映射或匿名映射实现共享内存
特点:
- 使用
/dev/zero
映射的内存,自动被初始化为0。 - 使用匿名映射方式不需要借助文件进行映射,需要借助
mmap
的MAP_ANONYMOUS
标志。 - 不限制映射内存的大小,但只能在只能在有亲子关系的进程之间实现通信。
另外,使用 mmap
映射之后,可以将文件关闭。由于映射内存是进程相关的,所以如果不调用 munmap
,进程退出时相应的映射也会自动消失(相当自动调用 munmap
),所以有的进程并没有调用过 munmap
。想要使用共享内存的特性,需要借助 MAP_SHARED
标记。
更多介绍参考:http://blog.sina.com.cn/s/blog_6a1837e90100o9vl.html
(2)实例代码
3、其它
待更新
coding
需要确定的问题:
-
getshm
的时候,如果已经存在一个关键字的时候,是否在创建关键字的时候返回错误? - 在
getshm
的时候,如果get
没有创建的共享内存,并且没有create
标志,那么返回的是错误还是尝试建立一个共享内存区域? -
keyid
是负数可以吗?