文件的内存映射
为什么要将文件映射到内存?
频繁地读取和写入文件十分耗时,如果能将文件加载到内存中,则读取和写入的主体就变成了程序和内存而不是程序和文件,大大减小时间开销。另外,将文件映射到内存中并由多个进程访问,能够实现进程之间共享内存。
函数和主要参数
#include <sys/mman.h>
/**
* @param start 映射内存的起始地址,通常设为NULL或0,表示由系统自动分配
* @param length 写入数据的长度
* @param prot 映射区的保护方式
* @param flags 映射区类型以及是否共享
* @param fd 文件描述符
* @param offset 映射数据在文件中的起点
*/
void* mmap(void* start, size_t length, int prot, int flags, int fd, off_t offset);
prot | 意义 |
---|---|
PROT_EXEC | 映射区可被执行 |
PROT_READ | 映射区可被读取 |
PROT_WRITE | 映射区可被写入 |
PROT_NONE | 映射区不可访问 |
flags | 意义 |
---|---|
MAP_FIXED | 在指定参数start时使用,若无法映射到start区域,则映射失败 |
MAP_SHARED | 共享映射区域,对映射区域写入将会写入到原文件中 |
MAP_PRIVATE | 为进程创建一个独享映射,修改映射区域不会保存到原文件中 |
MAP_ANONYMOUS | 建立匿名映射。映射区不与任何文件关联,无法共享 |
MAP_DENYWRITE | 对文件的写入操作将被禁止 |
MAP_LOCKED | 锁定映射区,防止映射区的内存被交换 |
flag必须为MAP_SHARED或MAP_PRIVATE二者之一,其他类型需与此二者搭配使用。
通过内存映射读取文件
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
int main(int argc, char** argv){
int fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
char* mappedMem, * p;
int flength = 1024;
void* startAddr = 0;
if(fd>0){
flength = lseek(fd, 1, SEEK_END);
write(fd, "\0", 1);
lseek(fd, 0, SEEK_SET);
mappedMem = (char*)mmap(startAddr, flength, PROT_READ, MAP_PRIVATE, fd, 0);
printf("%s\n", mappedMem);
close(fd);
munmap(mappedMem, flength);
}
return 0;
}
I have an apple.
先向文件中追加了一个字符’\0’,来满足printf打印所需。在mmap函数中,我们设置prot参数为PROT_READ,表示这块内存映射可以读取;设置flags参数为MAP_PRIVATE,表示进程独享该内存映射,并且不将内存映射中的修改写入原文件(时间上此处由于prot参数的问题也无法修改)。
执行后成功将文件内容打印。
通过内存映射修改文件
此处只需要修改通过内存映射读取文件中的prot和flag参数,就可以让我们具有权限去实现修改内存映射,并将内存映射保存到原文件中。
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
int main(int argc, char** argv){
int fd = open(argv[1], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
char* mappedMem, * p;
int flength = 1024;
void* startAddr = 0;
if(fd>0){
flength = lseek(fd, 1, SEEK_END);
write(fd, "\0", 1);
lseek(fd, 0, SEEK_SET);
mappedMem = (char*)mmap(startAddr, flength, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
printf("%s\n", mappedMem);
p = strstr(mappedMem, "apple");
memcpy(p, "linux", 5);
close(fd);
munmap(mappedMem, flength);
}
return 0;
}
I have an apple.
之后在终端中执行:
$ cat myFile.txt
I have an linux.