共享内存是IPC形式中最快的方式。一旦将这样的内存区映射到共享它的进程的地址空间,这些进程间数据的传递就再也不涉及内核。然而往该共享内存区存放信息或从中取走信息的进程间一般须要某种方式的同步。函数
实现共享内存的方式:利用mmap函数、使用Posix共享内存区、使用SystemV共享内存区,下面分别介绍。ui
做用:open文件以后调用mmap把它映射到调用进程地址空间的某个文件。以后全部的I/O都在内核的掩盖下完成,只需编写存取映射区中各个值的代码便可,而没必要调用read、write或lseek进行操做。线程
须要说明的一点:并非全部文件都能进行内存映射,如终端和套接字的描述符不支持内存映射,试图将其映射到内存将致使mmap返回一个错误。这些类型的描述符必须使用read和write(或者相关变体)来访问。指针
相关函数:mmap,mumap(删除共享内存)及msync(用于同步共享内存和文件的内容),下面仅介绍mmap。对象
void *mmap(void *addr, size_t len, intprot, inf flag, off_t offset);进程
返回值:成功返回被映射区的起始地址,失败返回MAP_FAILED内存
参数说明:get
addr指定描述符fd应被映射到的进程内空间的起始地址,为了保证可移植性,一般被指定为一个空指针,这样就有内核自行选择起始地址。cmd
len参数为映射到进程地址空间中的 字节数,它从被映射文件开头的offset个字节处开始算。同步
prot参数指定了映射内存的读、写、执行等相关权限,取值可为PROT_READ、PROT_WRITE、PROT_EXEC、PROT_NONE(不可访问)
flag指示该共享内存的做用效果,一般取为MAP_SHARED(变量是共享的,调用进程对被映射数据所做的修改对于共享该对象的全部进程可见,并改变了底层支持对象——一般是文件);若是指定为MAP_PRIVATE,那么调用进程对被映射数据所作的修改只对该进程可见,同时也不改变底层支持对象;以上两个标志必须指定一个,当须要准确解释addr指向的地址参数时或上MAP_FIXED。
做用:分别用于把一个文件或一个Posix共享内存区对象映射到调用进程的地址空间,有三种目的:
1) 使用普通文件以提供内存映射I/O(应用最普遍)
2) 使用特殊文件以提供匿名内存映射
有两种方法:
4.4BSD提供匿名内存映射,避免了文件的建立和打开。其经过把mmap的flag参数指定成MAP_SHARED|MAP_ANON,把fd参数指定为-1.忽略offset参数。这样的内存区被初始化为0。(由fork或具备亲缘关系的线程使用)
SVR4提供了/dev/zero设备文件,open它以后可使用获得的文件描述符,而后里面mmap进行内存映射,以后执行关闭便可。这样的映射保证内存映射区被初始化为0(可由无亲缘关系的进程使用)
3) 使用shm_open以提供无亲缘关系进程间的Posix共享内存区
访问共享内存时,文件大小和内存映射大小能够不一样,下面进行访问状况的两点说明:
a) 文件大小等于内存映射区的大小:只能访问相应大小的共享内存,超出的话会引起SIGSEGV信号
b) 文件大小小于共享内存区的大小:只能访问文件大小+最后一页上该对象以远的那些字节,访问超过的内存(超过文件大小但小于共享内存大小)时,会引起SIGBUS信号。若想访问这部份内存,须要增长文件大小,可用lseek或者ftruncate函数调整文件大小。
Posix共享内存是由shm_open打开一个Posix IPC名字(也许是在文件系统中的一个文件),所返回的描述符由mmap函数映射到当前进程的地址空间。即涉及如下步骤:
一、 指定一个名字参数调用shm_open,以建立一个新的共享内存区对象或打开一个已存在的共享内存区对象
二、 调用mmap把这个共享内存区映射到调用进程的地址空间
相关函数:
头文件:#include <sys/mman.h>
建立或打开共享内存区对象:
int shm_open(const char *name, into flag,mode_t mode) //成功返回非负描述符,若出错则为-1,name为绝对路径的Posix IPC名字形式
删除共享内存区对象:
int shm_unlink(const char *name) //成功返回0,失败-1
p.s:当调用mmap完成内存映射以后就能够关闭描述符了,由于已经获得共享内存区的指针了;以后的操做同mmap。与mmap映射文件实现共享内存基本相似,不一样的是获取文件描述符的方式。
SystemV共享内存区在概念上相似与Posix共享内存区。其获取共享内存区的方式为先调用shmget,再调用shmat,对于每一个共享内存区,内存维护一个shmid_ds对象
相关函数:
头文件 #include <sys/shm.h>
1) 建立或打开:
int shmget(key_t key, size_t size, intoflag) //若成功则为共享内存区对象,若出错则为-1;其中key能够是ftok函数的返回值,也能够是IPC_PRIVATE;size指示了共享内存区的大小(以字节为单位),当实际操做为访问一个已存在的共享内存区时,应该为0,而建立时则应大于0
p.s:shmget并无给调用进程提供访问该内存区的手段,故须要调用shmat
2) 附加共享内存区到调用进程的地址空间:
void *shmat(int shmid, const void *shmaddr,int flag); //若成功则返回映射区的起始地址,若出错则为-1;shmaddr通常取为0,这样系统会替调用者选择地址,这是推荐的的方法(可移植性好),若不为0的话则附加由shmaddr参数指定的地址,这时还和flag参数有关
断开共享内存(当使用完后调用):
int shmdt(const void * shmaddr); //成功返回0,失败返回-1
p.s:当进程终止时,它当前附接着的全部共享内存区都自动断接掉。函数并不会删除共享内存,执行删除的话须要以参数为IPC_RMID调用shmdt
3) 控制操做:
int shmctl(int shmid, int cmd, structshmid_ds * buff); //若成功返回0,若出错为-1
当cmd取不一样值时,函数能够实现三个功能以下:
IPC_RMID:从系统中删除指定的共享内存,这时buff参数置空
IPC_SET:给所指定的共享内存区设置其shmid_ds结构的如下三个成员:shm_perm.uid,shm_perm.did及shm_perm.mode,它们的值来自buff参数的相应成员。shm_ctime的值也用当前时间替换。
IPC_STAT:获取所指定共享内存当前的shmid_ds结构
与System V消息对了和System V信号量同样,System V共享内存区也存在特定的系统限制。能够在共享内存相关manual 文件中查看,如man shmget