当前位置: 首页 > news >正文

【Linux文件映射 -- mmap基本介绍】

文件映射的引入

在传统的文件读写中,例如说我们使用read/write操作文件时,它们会进行如下操作

  • 用户态切换为内核态
  • 将文件内容拷贝至自己的内核缓冲区
  • 将内核缓冲区的内容拷贝至用户层缓冲区
  • 内核态切换为用户态

在上述操作中,主要涉及到的时间成本是系统调用导致的状态变化以及拷贝所带来的消耗

为了解决上述问题,Linux中提供了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:表示需要映射的字节数
  • prot:表示该映射空间的权限,常用选项:PROT_READ(读)、PROT_WRITE(写)、PROT_EXEC(可执行)
  • flags:表示映射类型和其他选项,常用选项为:MAP_PRIVATE(私有映射)、MAP_SHARED(共享映射)、MAP_ANONYMOUS(匿名映射)
  • fd:需要映射的文件描述符
  • offset:映射文件的起始偏移量,即映射区域的开始位置
  • 返回值为MAP_FAILED表示失败,否则返回成功映射空间的起始地址

 注意:length长度字段必须设置为页面大小的整数倍,若不是页面大小的整数倍,那么mmap会自动向上取整,例如我当前系统的页面大小是4096字节,那么我传递1000时,mmap会自动映射出4096的大小

页面大小:由操作系统内存管理单元(MMU)定义,是虚拟内存分页的最小单位(如4KB、2MB等)。其核心作用是优化物理内存的使用,支持虚拟地址到物理地址的映射,减少内存碎片。


munmap系统调用所在的头文件是:#include<sys/mman.h>

功能:将一个映射文件进行去映射

如下是它的接口:

int munmap(void *addr, size_t length);
  • addr:即mmap返回的映射空间的起始地址
  • length:即映射空间的字节数
  • 若返回值为-1则表示去映射失败,返回值为0表示成功

注意:munmap传递length时内部处理也是按照页面大小来处理的,换句话来说会向上取整到4096字节的整数倍 

扩展:

ftruncate系统调用所在的头文件是:<sys/types.h>和<unistd.h>

功能是调整一个文件的大小(若调整的大小比未调整之前大,则多出来的用0填充)

如下是它的接口介绍:

int ftruncate(int fd, off_t length);
  • fd:需要调整大小的文件的文件描述符
  • length:调整后的大小
  • 返回值为-1表示调整失败,等于0表示成功

fstat系统调用所在的头文件是:<sys/types.h>和<sys/stat.h>和<unistd.h> 

功能是获取一个文件的属性

如下是它的接口介绍

int fstat(int fd, struct stat *statbuf);
  • fd:对应文件的文件描述符
  • statbuf:输出型参数,该结构体中包含了文件的属性
  • 返回值为0表示获取成功,否则表示失败 

 struct stat结构体中的属性如下:

               dev_t     st_dev;         /* ID of device containing file */ino_t     st_ino;         /* Inode number */mode_t    st_mode;        /* File type and mode */nlink_t   st_nlink;       /* Number of hard links */uid_t     st_uid;         /* User ID of owner */gid_t     st_gid;         /* Group ID of owner */dev_t     st_rdev;        /* Device ID (if special file) */off_t     st_size;        /* Total size, in bytes */blksize_t st_blksize;     /* Block size for filesystem I/O */blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

文件写入映射的步骤与demo 

编程一个文件写入映射代码如下步骤:

  • 打开目标文件(mmap需要你自己先打开文件)
  • 手动调整文件大小,否则可能造成非法映射(例如你文件的大小只有1000,而映射时只能是页面整数倍,造成非法映射)
  • 文件映射
  • 进行文件操作
  • 关闭映射
  • 关闭文件

假设我们现在需要通过命令行参数传入一个文件名,之后对该文件写入a-z字符,则可编写出如下demo

#include <iostream>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#define PAGE_SIZE 4096
//命令行参数中输入文件名,通过mmap对文件内容进行操作
int main(int argc , char* argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << " filename" << std::endl;return 1;}//1.打开目标文件.mmap需要你自己先打开文件int fd = ::open(argv[1] , O_RDWR | O_CREAT | O_TRUNC , 0666);if(fd < 0){std::cerr << "open file error!" << std::endl;return 2;}//2.手动调整文件的大小为PAGE_SIZE,方便我们进行合法映射//ftruncate调整大小后,默认是用0填充的if(::ftruncate(fd,PAGE_SIZE) < 0){std::cerr << "Failed to ftruncate file !" << std::endl;return 3;}//3. 文件映射char* shmaddr = (char*)::mmap(nullptr , PAGE_SIZE , PROT_READ | PROT_WRITE , MAP_SHARED , fd , 0);if(shmaddr == MAP_FAILED){std::cerr << "Failed to mmap file" << std::endl;return 4;}//4.进行文件操作for(char c = 'a' ; c <= 'z' ; ++c){*(shmaddr + c - 'a') = c;}//5.关闭映射if(::munmap(shmaddr,PAGE_SIZE) < 0){std::cerr << "Failed to munmap file" << std::endl;return 5;}//关闭文件close(fd);return 0;
}

文件读取映射的步骤与demo

编程一个文件写入映射代码如下步骤:

  • 打开目标文件
  • 获取文件大小
  • 文件映射
  • 进行文件操作
  • 关闭映射
  • 关闭文件

 假设我们现在需要通过命令行参数传入一个文件名,之后读取该文件内容,则可编写出如下demo

#include <iostream>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
//命令行参数中输入文件名,通过mmap映射到文件后,读取文件内容
int main(int argc , char* argv[])
{if(argc != 2){std::cout << "Usage: " << argv[0] << " filename" << std::endl;return 1;}//1.打开目标文件.mmap需要你自己先打开文件int fd = ::open(argv[1] , O_RDONLY);if(fd < 0){std::cerr << "open file error!" << std::endl;return 2;}//2.获取文件大小struct stat st;::fstat(fd,&st);//3. 文件映射char* shmaddr = (char*)::mmap(nullptr , st.st_size , PROT_READ , MAP_SHARED , fd , 0);if(shmaddr == MAP_FAILED){std::cerr << "Failed to mmap file" << std::endl;return 4;}//4.进行文件操作std::cout << "File Content : " << shmaddr << std::endl;//5.关闭映射if(::munmap(shmaddr,st.st_size) < 0){std::cerr << "Failed to munmap file" << std::endl;return 5;}//关闭文件close(fd);return 0;
}

mmap扩展 -- 申请内存空间

mmap的flags参数我们在进行传递时一般都是传递MAP_SHARED

接下来看看对他flags参数其他选项的介绍:

  • MAP_PRIVATE:创建一个私有的内存映射,进程对映射内存的修改不会同步到文件或其他进程,且对其他映射同一文件的进程不可见。
  • MAP_ANONYMOUS:创建一块不与任何文件关联的匿名内存区域,内容初始化为零,通常用于动态分配大块内存。

通过这两个选项"|"的方式,我们可以实现映射一块不与任何文件关联的私有的内存映射,实际上就是说的开辟内存。

实际上malloc底层很大一部分的实现都是基于mmap的,除此之外还有brk(感兴趣了解下)

我们可以使用mmap实现一个精简版的malloc和free

注意:对于匿名映射并不会关联到文件,设置为-1即可

上述这种申请内存方式,可以用于一次申请大量内存,不适用于小内存的申请,因为mmap强制分配以页大小倍数为单位!

http://www.dtcms.com/a/423016.html

相关文章:

  • C++设计模式_结构型模式_适配器模式Adapter
  • 缓存总线是什么?
  • 无锡漫途科技大型平原灌区水量调度一体化智慧监测方案
  • 专注创新,守护安全——新天力科技以领先技术引领食品包装行业
  • ARM芯片架构之DAP:AXI-AP 技术详解
  • ARM芯片架构之调试访问端口(DAP)
  • 企业门户网站管理办法ceo是什么意思是什么职位
  • Java SE “核心类:String/Integer/Object”面试清单(含超通俗生活案例与深度理解)
  • ChatGPT From Zero To Hero - LLM学习笔记(一)
  • 县城网站怎么做英文网页
  • ASP.NET Core 10.0 的主要变化
  • 广州市公司网站建设品牌广州建设网站是什么样的
  • 电感损耗计算方法梳理
  • openEuler - 初探Chrony时间同步服务
  • 【C语言代码】大小写转换
  • 网站开发 程序开发阶段建设公众号官方网站
  • 泰安市建设信息网站哈尔滨通用建设工程有限公司
  • 3dgs项目详解 :convert.py
  • 大模型落地实践指南:从技术路径到企业级解决方案
  • 2004 年真题配套词汇单词笔记(考研真相)
  • Java面经(22届考研-华oD)
  • VS Code 使用 Chrome DevTools MCP 实现浏览器自动化
  • MySQL笔记---内置函数
  • 紫外UV固化太阳光模拟器的原理
  • 南京网站建设哪家好简洁 网站模板
  • react 初体验
  • 南华 NHXJ-02 汽车悬架检验台:技术特性与实操应用指南
  • 鄂湘赣新能源汽车产业质量技术创新联合体成立大会暨汽车网络安全标准专题培训会在汉圆满召开
  • 物联网智能安防系统
  • 洗头竖鞋带名片改良授权做网站不贵高端产品网站建设