学习这篇博客,进行了一些归纳Linux下mmap_linux mmap_一个山里的少年的博客-CSDN博客https://blog.csdn.net/qq_56999918/article/details/127070280
>>读取文件
读取文件方法:由操作系统提供的两个方法,read和write来读写文件。
由于read和write是系统调用,需要先从用户态进入到内核态,再将磁盘中的数据拷贝到操作系统的缓冲区中,然后再将缓冲区中的数据拷贝到用户态中,这个过程进行了两次拷贝。
>>mmap
① memory map:是一种内存映射文件的方法。
② mmap是一个可以将一个文件或者其他对象映射到进程的地址空间实现磁盘的地址和进程虚拟地址空间一段虚拟地址的一一对应关系。
③ mmap系统调用可以让进程之间通过映射到同一个普通文件实现共享内存,普通文件被映射到进程地址空间当中,之后进程可以向访问普通内存一样对文件进行一系列操作。
【总结】
① 日常中使用read或者write需要进行两次拷贝,一次是将文件拷贝到内核缓冲区,一次是从内核缓冲区拷贝到用户缓冲区。
② 使用mmap可以减少第二次拷贝,由于内核将文件映射到内存,之后用户进程就可以操作这些数据了,用户进程只需要修改内核中的内容,接着通过内核的内存管理器自动将这些数据刷新到磁盘当中。
③ mmap可以提高内存性能,内核空间和用户空间共用一个缓冲区,当多个进程正在对同一个文件进行IO操作,那么通过使用mmap能够共享一个内核缓冲区,从而可以减少内存消耗。
用户态中的mmap函数以及相关参数详解:
mmap的使用
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
函数说明:创建虚拟内存到物理内存或者文件的映射
参数:
- addr:映射区的起始地址,如果是NULL,系统自动分配
- length:字节长度自动按照4KB对齐,建议大小一般填成4KB的整数倍
- prot:映射区域的权限
- flags:映射的标志位
- fd:文件描述符
- offset:文件偏移量自动按照4KB对齐
prot 的取值:
PORT_EXEC:映射的区域具有可执行权限
PROT_READ:映射的区域具有可读权限
PROT_WRITE:映射区域具有可写权限
PROT_NONE:映射区域不可被访问
对应flags的取值:
MAP_SHARED:对映射区域的写入操作直接反映到文件中
MAP_FIXED:若在start上无法创建映射则失败(如果没有此标记会自动创建)
MAP_PRIVATE:对映射区域的写入操作只反映到缓冲区当中不会写入到真正的文件
MAP_ANONYMOUS:匿名映射将虚拟地址映射到物理内存而不是文件(忽略fd)
MAP_DENYWRITE:拒绝其它文件的写入操作
MAP_LOCKED:锁定映射区域保证其不被置换
返回值:函数的返回值为最后文件映射到进程空间的地址,进程可直接操作起始地址为该值的有效地址
内核态中的mmap函数:
int mmap(struct file* filp,struct vm_area_struct* vma),
>>用户态的mmap函数实现映射到物理内存
#include <iostream>
#include <sys/mman.h>
#include <cstring>
#include <cerrno>
#include <cstdio>
using namespace std;
static const int SIZE = 4096;
int main()
{
char *str = (char *)mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, 0, 0);
//注意MAP_PRIVATE和MAP_SHARED
//建立映射
if (str == MAP_FAILED)
{
printf("%s\n", strerror(errno));
return -2;
}
strcpy(str, "hello ksy");
puts(str);
//用于取消映射
munmap(str, SIZE);
return 0;
}
>>用户态的mmap函数实现映射到文件
#include <iostream>
#include <sys/mman.h>
#include <cstring>
#include <cerrno>
#include <cstdio>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
using namespace std;
static const int SIZE = 4096;
int main()
{
int fd = open("./a.txt", O_RDWR | O_CREAT, 0644);
truncate("a.txt", 1024);
if (fd < 0)
{
printf("%s\n", strerror(errno));
return -1;
}
char *str = (char *)mmap(NULL, SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
//注意MAP_PRIVATE和MAP_SHARED
//建立映射
if (str == MAP_FAILED)
{
printf("%s\n", strerror(errno));
close(fd);
return -2;
}
strcpy(str, "helloworld");
close(fd);
return 0;
}
【注意】
如果不加truncate("a.txt", 1024);会出现总线错误 (核心已转储)
【原因】mmap是虚拟内存映射到文件(物理内存)。由于heheda.txt是新创建的,也就是0个字节。那么在映射时候也是映射了0个字节,这个文件映射过来的内存是没有的,所以我们往里面写东西就会崩溃,truncate函数对文件提前处理一下:
int truncate(const char *path, off_t length);
函数说明:truncate()会将参数path指定的文件大小改为参数length指定的大小。
如果原来的文件大小比参数length大,则超过的部分会被删除。可以提前使用这个函数提前将文件的大小进行设置,就可以向映射的这块内存进行写入了。也就可以成功将其写入到文件中了