1.基本概念
存储映射I/O(memory-mapped I/O)能将一个磁盘文件映射到存储空间中的一个缓冲区上。
于是,当从缓冲区中取数据是,就相当于读文件中的相应字节。与此类似,将数据存入缓冲区时,相应字节就自动写入文件。这样就可以在不使用read和write的情况下执行I/O。
为实现该功能,应首先告诉内核将一个给定的文件映射到一个存储区域中。
原理如下:
2.mmap函数完成该功能
该函数参数较多
首先对于内存而言:
addr参数,用于指定映射存储区的起始地址。通常设置为0(NULL)
我们知道内存地址不能是0地址,也不能3G到4G。
设为0,系统会找一块附近空闲的内存。而该函数的返回值(若成功)即映射区的起始地址。
len参数,申请的字节长度
prot参数,映射区保护要求
flags参数,很重要
分为MAP_SHARED和MAP_PRIVATE
共享映射或私有映射
共享:内存的修改,磁盘也跟着修改。磁盘修改,内存也跟着修改。私有则不会,各自为私有数据。
后两个对于磁盘文件而言:
fd,文件描述符
offset,偏移量,以页为单位,4096字节的倍数。
若返回失败,则返回 MAP_FAILED宏。
失败可能:
内存空间不够
fd文件描述符不存在
或者文件只有读权限,而在内存中去写
注意:
若关闭文件,不会解除映射,
close只是把file结构体的引用计数减1,但映射机制和file结构体没有关系
最后一定要养成良好习惯,申请完要用munmap函数释放,否则会内存泄漏
3.实例
#include<stdio.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<sys/mman.h>
#include<stdlib.h>
int main(void){
int fd,len;
int *p;
fd=open("hello",O_RDWR);
if(fd<0){
perror("open");
exit(1);
}
len=lseek(fd,0,SEEK_END);//计算长度
//fstat
p=mmap(NULL,len,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
if(p==MAP_FAILED){
perror("mmap");
exit(1);
}
//用的是MAP_SHARED,把内存改了,磁盘文件也会跟着改
close(fd);
p[0]=0x30313233;//如果成功,p[0]即索引前四个字节
//p指向内存的首地址
//对应字符0123
munmap(p,len);
return 0;
}
执行程序后,成功把文件修改了。
4.怎么用mmap实现进程间通信呢?
不难想到,让一个磁盘文件同时映射两个进程。都设置为MAP_SHARED 。
一个进程修改数据,磁盘文件数据修改,然后另一个进程的数据也会修改。