目录
1.前言
本篇文章的所有例子,基于RHEL6.5平台(linux kernal: 2.6.32-431.el6.i686)。
2.介绍
共享内存也是一种IPC,它是目前最快的IPC,它的使用方式是将同一个内存区映射到共享它的不同进程的地址空间中,这样这些进程间的通信就不再需要通过内核,只需对该共享的内存区域进程操作就可以了。
图1:共享内存区进程间通信(IPC)
共享内存与其他的进程间通信最大的优点是:数据的复制只有两次,一次是从输入文件到共享内存区,一次从共享内存区到输出文件。而其他的则是需要复制4次:服务器将输入文件读入自己的进程空间,再从自己的进程空间写入管道/消息队列等;客户进程从管道/消息队列中读出数据到自己的进程空间,最后输出到客户指定的文件中。
要使用共享内存,应该有如下步骤:
1.开辟一块共享内存 shmget()
2.允许本进程使用共某块共享内存 shmat()
3.写入/读出
4.禁止本进程使用这块共享内存 shmdt()
5.删除这块共享内存 shmctl()或者使用命令行ipcrm
前一篇的文章中已经介绍过了Posix共享内存《进程间通信(8) - 共享内存(posix)》,System V共享内存区在概念上与Poisx共享内存区类似,Posix共享内存区的使用是调用shm_open创建共享内存区后调用mmap进行内存区的映射,而System V共享内存区则是调用shmget创建共享内存区然后调用shmat进行内存区的映射。
对每个System V共享内存区,内核会维护一个shmid_ds的数据结构。
Linux 2.6.32中的定义如下,参考/usr/include/sys/shm.h。
#include<sys/shm.h>
typedef unsigned long int shmatt_t; //连接共享内存区的进程数量的数据类型
struct shmid_ds
{
struct ipc_perm shm_perm; //operation permission struct
size_t shm_segsz; //共享存储段的最大字节数
__time_t shm_atime; //time of last shmat()
__time_t shm_dtime; //time of last shmdt()
__time_t shm_ctime; //time of last change by shmctl()
__pid_t shm_cpid; //pid of creator
__pid_t shm_lpid; //pid of last shmop
shmatt_t shm_nattch; //连接共享内存区的进程数
//保留字段
unsigned long int __unused1;
unsigned long int __unused2;
unsigned long int __unused3;
unsigned long int __unused4;
unsigned long int __unused5;
};
3.创建共享内存shmget
#include <sys/shm.h>
int shmget( key_t key, size_t size , int shmflg); //成功返回共享内存标识符,失败返回-1
shmget函数用于创建或打开一个共享内存区对象,shmget成功调用会返回一个共享内存区的标识符,供其它的共享内存区操作函数使用。
参数key:用于创建共享内存区的键值,这个在前面其他System IPC创建的时候已经讨论过了,System IPC都有一个key,作为IPC的外部标识符,创建成功后返回的描述符作为IPC的内部标识符使用。key的主要目的就是使不同进程在同一IPC汇合。
它是这块共享内存的标识符。如果是父子关系的进程间通信的话,这个标识符用IPC_PRIVATE来代替。
key具体说可以有三种方式生成:
· 不同的进程约定好的一个值;
· 通过相同的路径名和项目ID,调用ftok()函数,生成一个键;
· 还可以设置为IPC_PRIVATE,这样就会创建一个新的,唯一的IPC对象;然后将返回的描述符通过某种方式传递给其他进程;
参数size:指定创建共享内存区的大小,单位是字节。如果实际操作为创建一个共享内存区时,必须指定一个非0值,如果实际操作是访问一个已存在的共