首页 > 代码库 > 进程间通信(三)——共享内存区

进程间通信(三)——共享内存区

1.概述

共享内存区是IPC中最快的,当内存区映射到共享它的进程的地址空间,进程间数据的传递就不再涉及内核。

但是这需要某种形式的同步,最常用的是信号量。

不再涉及内核:进程不再通过执行任何进入内核的系统调用来彼此传递数据。内核必须建立允许各个进程共享该内存区的内存映射关系,然后一值管理该内存区。

 

管道,FIFO和消息队列的问题是,两个进程要交换信息时,这些信息必须经由内核传递。

共享内存区可以绕过这个问题,但是一般必须同步数据。

 

使用内存映射文件的特性,所有的I/O都不再有内核直接参与IO,我们绝不调用read,write,lseek,可以简化我们的代码。

不是所有的文件都能进行内存映射,例如:访问终端或套接字描述符就不可以,这些类型的描述符必须使用read和write来访问。

 

2.相关函数

mmap把文件或Posix共享内存区对象映射到调用进程的地址空间。

三个目的:

(1) 使用普通文件,内存映射IO, open,mmap()方式,也适用于无亲缘关系的进程间。

(2) 使用特殊文件,匿名内存映射,mmap(O_ANON)或open(/dev/zero), mmap()方式,只适用于有亲缘关系的进程间。

(3) 使用shm_open提供无亲缘关系的进程间的Posix共享内存区,shm_open, mmap方式,适用于无亲缘关系的进程间。

#include <sys/mman.h>

void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);

成功:被映射区的起始地址; 出错:MAP_FAILED

addr: 指定fd描述符设置到进程内空间的起始地址,通常NULL,由内核选择起始地址。

len: 映射到调用进程地址空间的字节数。从offset计数。

offset:通常0。文件映射内存的偏移量。

prot: 内存映射区的保护,通常PROT_READ | PROT_WRITE

flags: 可以指定MAP_PRIVATE:调用进程对被映射数据的修改只对该进程可见。

        MAP_PRIVATE: 对共享该数据的所有进程可见。

mmap成功后,fd可以关闭,映射已经建立。

2014-07-30_213256

2014-07-30_213347

 

从进程地址空间输出一个映射关系

#include <sys/mman.h>

int munmap(void *addr, size_t len);

 

使硬盘上的文件与内存映射区的内容一致

int msync(void *addr, size_t len, int flags);

0;-1

flags: MS_ASYNC:异步写

        MS_SYNC: 同步写

        MS_INVALIDATE: 使高速缓存失效

3.匿名映射

若调用mmap目的是提供一个将穿越fork由父子进程共享的映射内存区,即在只是在有亲缘关系的内存间共享内存,可以使用匿名映射。

(1) 4.4BSD提供了匿名内存内存映射,避免了文件的创建和打开。

通过flags指定MAP_SHARED | MAP_ANON, fd = -1, offset = 0。这样的内存去区初始化为0。

int *ip = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, –1, 0);

(2) SVR4 提供/dev/zero设备文件,open它之后可在mmap中使用得到的描述符。

从设备读时返回的字节全是0,写往该设备的任何字节则被丢弃。许多BSD的也支持这个。

int fd = open(“/dev/zero”, O_RDWR);

int *ip = mmap(NULL, sizeof(int), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);

 

4. Posix共享内存区

open——mmap:posix内存映射文件;

shm_open——mmap: posix共享内存区对象。

这两者叫做:Posix内存区对象。

4.1. 打开,删除

posix共享内存区需两个步骤:

(1) 指定名字参数调用shm_open,以创建一个新的共享内存区对象或打开一个已存在的共享内存区对象。

(2) 调用mmap把这个共享内存区映射到进程的地址空间。

#include <sys/mman.h>

int shm_open(const char *name, int oflag, mode_t mode);

成功:非负描述符; 出错:-1;

删除一个共享内存区对象的名字。不会影响对于其底层支撑对象的现有应用,直到对于该对象的应用全部关闭为止。

int shm_unlink(const char *name);

 

改变普通文件或共享内存区对象的大小

#include <unistd.h>

int ftruncate(int fd, off_t length);

 

已存在的共享内存区对象,或取对象大小

#include <sys/type.h>

#include <sys/stat.h>

int fstat(int fd, struct stat *buf);

struct stat {

mode_t st_mode;

uid_t    st_uid;

gid_t    st_gid;

off_t     st_size;    //size in bytes
};