基本概述
内存映射就是将虚拟内存中的一块区域与磁盘上的对象建立关联以初始化虚拟内存区域的内容。有两种映射
- 文件映射:讲一个文件的一部分直接映射到调用进程的虚拟内存中
- 匿名映射:一个映射没有对应的文件(也可以理解成一个内容总是被初始化为零的虚拟文件的映射)
一个进程的映射中的内存可以与其他进程中的映射共享,当两个或者多个进程共享相同的物理分页时候,每个进程都可以对其做修改和读取,此时就会出现一致性问题,由此,映射的方法又可以分为共享和私有:
- 私有映射:在映射内容上发生的变更对其他进程不可见,对于文件映射来说即为不会在物理页面(底层)更改。此时就会利用写时复制技术来实现
- 共享映射:在映射内容上发生的变更会对所有共享同一个映射的其他进程可见
#include <sys/man.h>
void *mmap(void *addr,size_t length,int prot,int flags,int fd,off_t offset);
//成功时候返回映射的地址,失败时返回MAP_FAILED
//addr参数指定映射时候被放置的虚拟地址,如果值为NULL则内核自动分配(推荐)
//length参数指定映射的字节数
//prot参数指定映射方式PROT_NONE(区域无法访问),PROT_READ,PROT_WRITE,PROT_EXEC
//flags是MAP_PRIVATE和MAP_SHARED
//最后两个参数是文件映射才有的,文件描述符和文件偏移映射起点
写时复制
写时复制技术是利用在私有映射的一种更改映射内容的方法。如下图所示,当两个进程将一个私有对象映射到他们虚拟内存的不同区域,但是共享这个对象的同一个物理副本。
当没有进程试图写自己的私有区域的某个页面时,就会一直共享着物理内存中对象的一个单独副本。一旦有一个进程要写共享着的某个页面时候,故障处理程序就会在物理内存中创建这个页面的一个新副本,更新页表条目指向这个新的副本,然后恢复这个页面的可写权限,因此进程可在这个新的页面上执行写操作。
#include <stdio.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdlib.h>
//相当于cat的功能,私有文件映射(写时复制技术)
int main(int argc,char *argv[]){
if(argc!=2){
printf("error argc\n");
}
int fd=open(argv[1],O_RDONLY);
if(fd==-1){
printf("open file error\n");
}
struct stat sb;
if(fstat(fd,&sb)==-1){
printf("fstat error\n");
}
char *addr=mmap(NULL,sb.st_size,PROT_READ,MAP_PRIVATE,fd,0);
if(addr==MAP_FAILED){
printf("mmap error\n");
}
if(write(1,addr,sb.st_size)!=sb.st_size){
printf("write error\n");
}
exit(0);
}
参考《csapp》《tlpi》《apue》