mmap 虚拟地址映射
mmap允许你将一个文件或设备直接映射到进程的虚拟地址空间,这样进程就能像访问普通内存一样读写文件内容,无需频繁调用
read
或write
等 I/O 函数
一.mmap与write/read的区别
对于read/write而言
对于一次文件的操作
进程调用
read
,陷入内核态。内核检查请求的文件数据是否在页缓存中。
若不在,内核从磁盘读取数据到页缓存。
内核将数据从页缓存拷贝到进程用户空间指定的缓冲区。
在这个拷贝过程中,CPU通过该进程的页表,将缓冲区的虚拟地址翻译为物理地址,从而写入数据。
需要经历以上5个操作
而对于mmap而言
它跳过了第4个阶段,也就是节省了拷贝的过程。会直接通过进程页表指向内核态的页缓存。
并且当你把建立虚拟地址映射后,你可以像操作指针一下对文件进行读写
可以看到对于文件的操作,大部分时候使用mmap会效率更高,但是一些大文件顺序读取的时候read/write可能会更优,因为他有缓冲区和预读机制。
二.mmap的使用
void *mmap(void addr[.length], size_t length, int prot, int flags,int fd, off_t offset);
简单的使用
1.对于flag 为Map_shared就相当于对文件进行映射
1).写
#include <sys/mman.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>#define PAGE_SIZE 4096
int main(int argc, char* argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << "filename" << std::endl;}//打开文件int fd = ::open(argv[1], O_RDWR | O_CREAT | O_TRUNC, 0666);//文件扩容--mmap不会对文件扩容::ftruncate(fd, PAGE_SIZE);//映射文件到内存 PAGE_SIZE传入必须要是 页大小的整数倍char* addr = (char*)::mmap(NULL, PAGE_SIZE, PROT_WRITE, MAP_SHARED, fd, 0);//写入数据for(char i = 'a' ; i <= 'z'; i++){addr[i-'a']=i;sleep(1);}//解除映射::munmap(addr, PAGE_SIZE);//关闭文件::close(fd);
}
log文件的每秒变化
2).读
#include <sys/mman.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>#define PAGE_SIZE 4096
int main(int argc, char* argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << "filename" << std::endl;}//打开文件int fd = ::open(argv[1], O_RDWR);struct stat st;::fstat(fd, &st);//映射文件到内存 PAGE_SIZE传入必须要是 页大小的整数倍char* addr = (char*)::mmap(NULL, st.st_size, PROT_READ, MAP_SHARED, fd, 0);//读取数据std::cout << "File Content: " << addr << std::endl;//解除映射::munmap(addr, PAGE_SIZE);//关闭文件::close(fd);
}
2.对于flags Map_private就相当于使用创建一块私有内存
对于malloc他的底层对于大块空间的获取,一般使用的就是mmap作为调用
#include <sys/mman.h>
#include <iostream>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <string.h>void* mmap_alloc(size_t size)
{void* addr = ::mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);return addr;
}
void mmap_free(void* addr, size_t size)
{::munmap(addr, size);
}
int main()
{char* addr = (char*)mmap_alloc(4096);memset(addr, 'A', 4096);std::cout<<std::endl;for(int i = 0; i < 4096; i++){printf("%c", addr[i]);fflush(stdout);sleep(1);}mmap_free(addr, 4096);
}
可以看到在虚拟映射表中多出了新的虚拟映射