概述
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据(如图)。
共享内存VS.其他IPC形式
用管道/消息队列传递数据
用共享内存传递数据
共享内存生成之后,传递数据并不需要再走Linux内核,共享内存允许两个或多个进程共享一个给定的存储区域,数据并不需要在多个进程之间进行复制,因此,共享内存的传输速度更快!
mmap内存映射
将文件/设备空间映射到共享内存区
#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);int munmap(void *addr, size_t length);
参数:
addr:要映射的起始地址,通常指定为NULL,让内核自动选择;
length:映射到进程地址空间的字节数;
prot:映射区保护方式(见下);
flags:标志(见下);
fd:文件描述符;
offset:从文件头开始的偏移量;
prot
说明
PROT_READ
页面可读
PROT_WRITE
页面可写
PROC_EXEC
页面可执行
PROC_NONE
页面不可访问
flags
说明
MAP_SHARED
变动是共享的
MAP_PRIVATE
变动是私有的
MAP_FIXED
准确解释addr参数,如果不指定该参数,则会以4K大小的内存进行对齐
MAP_ANONYMOUS
建立匿名映射区,不涉及文件
mmap返回值:
成功:返回映射到的内存区的起始地址;
失败:返回MAP_FAILED;
内存映射示意图:
(注意:内存映射时,是以页面(4K)作为单位)
/** 示例1: 写文件映射将文件以可读,可写的方式映射到进程的地址空间, 然后向其中写入内容**/struct Student{char name[4];int age;};int main(int argc,char **argv){if (argc != 2)err_quit("usage: ./main <file-name>");int fd = open(argv[1], O_CREAT|O_RDWR|O_TRUNC, 0666);if (fd == -1)err_exit("file open error");//为内存映射争取空间if (lseek(fd, sizeof(Student)*5-1, SEEK_SET) == (off_t)-1)err_exit("lseek error");write(fd, "", 1);Student *p = (Student *)mmap(NULL, sizeof(Student)*5,PROT_WRITE|PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 此时:操纵文件就可以如同操纵内存一样了char ch = ‘a’;for (int i = 0; i < 5; ++i){memcpy((p+i)->name, &ch, 1);(p+i)->age = 20+i;++ ch;}cout << "file initialized!" << endl;if (munmap(p, sizeof(Student)*5) == -1)err_exit("munmap error");cout << "process exit…" << endl;return 0;}/**示例2: 读文件映射**/int main(int argc,char **argv){if (argc != 2)err_quit("usage: ./main <file-name>");//以只读方式打开int fd = open(argv[1], O_RDONLY);if (fd == -1)err_exit("file open error");void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);Student *p = (Student *)mmap(NULL, sizeof(Student)*5,PROT_READ, MAP_SHARED, fd, 0);if (p == MAP_FAILED)err_exit("mmap error");// 从内存中读取数据(其实是从文件中读取)for (int i = 0; i < 5; ++i){cout << "name: " << (p+i)->name << ", age: " << (p+i)->age << endl;}if (munmap(p, sizeof(Student)*5) == -1)err_exit("munmap error");cout << "process exit…" << endl;return 0;}
map注意点:
1.内存映射不能(也不可能)改变文件的大小;
2.可用于进程间通信的有效地址空间不完全受限于映射文件的大小,而应该以内存页面的大小为准(见下面测试);
要纠正别人之前,先反省自己有没有犯错