【Linux】mmap文件内存映射

【Linux】mmap文件内存映射

📝前言:

这篇文章我们来讲讲Linux——mmap

  1. mmap介绍
  2. mmap接口介绍
  3. mmap使用示例
🎬个人简介:努力学习ing
📋个人专栏:Linux
🎀ZEEKLOG主页 愚润求学
🌄其他专栏:C++学习笔记C语言入门基础python入门基础C++刷题专栏

这里写目录标题

一,mmap介绍

1. 基本介绍

允许用户空间程序将文件或设备的内容直接映射到进程的虚拟地址空间中。(直接建立虚拟内存到文件页缓存的映射关系

在这里插入图片描述

2. mmap的优势

通过 系统调用mmap ,程序可以高效地访问文件数据,而无需通过传统的 readwrite 系统调用进行数据的复制

具体来说:

  • 传统的readwrite分为两步(下面以write为例)
    • 第一步:用户态空间缓冲区 → 内核页缓冲区(其实就是内核文件缓冲区)
    • 第二步:内核页缓冲区 → 磁盘
  • 使用mmap
    • 映射阶段:内核将文件映射到进程的虚拟地址空间,但物理内存尚未分配(讲页表的时候提到的:延迟申请,当真正访问阶段需要的时候才缺页中断分配)
    • 修改数据:进程直接修改映射的内存(相当于直接修改内核的页缓存),无需调用 write,也无用户态到内核态的数据拷贝

注意:当内核页缓存修改好了,我们就认为改好了,我们不关心内核缓冲区到磁盘的刷新这一过程(复杂)

二,接口介绍

当我们建立好映射以后,就可以直接对“开辟”的空间进行操作(虚拟地址)
我们对虚拟地址的操作,都是直接修改映射的内存。

1. mmap建立映射

头文件<sys/mman.h>

函数原型

void*mmap(void*addr, size_t length,int prot,int flags,int fd, off_t offset);

参数说明

  • addr:建议的映射起始地址(通常设为 NULL,由内核自动选择)
  • length:映射区域的长度(必须是页大小(4 KB)的整数倍,如 4096 字节)
  • prot:映射区的内存保护模式选项(用|链接)
    • PROT_READ:映射区可读
    • PROT_WRITE:映射区可写
    • PROT_EXEC:映射区可执行
    • 注意:映射权限必须 <= 文件打开权限
  • flags:映射类型(如 MAP_SHAREDMAP_PRIVATE
    • MAP_PRIVATE:创建⼀个私有映射。对映射区域的修改不会反映到底层文件中。(即:修改仅对当前进程有效(写时复制,类似 fork
    • MAP_SHARED:创建⼀个共享映射。对映射区域的修改会反映到底层文件中(即:修改会同步到文件,其他进程可见)
    • MAP_ANONYMOUS:指定要创建⼀个匿名内存映射
  • fd:文件描述符(匿名映射时设为 -1
  • offset 文件偏移量(开始映射的位置相较于0位置处的偏移)(必须是页大小的整数倍)

返回值

  • 成功:返回映射区的起始地址(虚拟地址)
  • 失败:返回(void*) -1 或者 MAP_FAILED(等效的)

注意

  • 映射的要是一个已经打开的文件!
  • 文件大小为 0 的文件无法映射,需要先调整文件大小
    • ftruncate(fd, SIZE)(会把文件的内容全部初始化成\0
  • 映射的长度如果 > 文件的大小,则可能导致未定义行为
  • 因为mmap需要读取文件元数据(如大小):所以,即使你只需要写入权限,也需要在open文件的时候赋予读权限

2. munmap取消映射

函数原型

intmunmap(void*addr, size_t length);

参数介绍

  • addr:映射空间的起始地址
  • length:空间长度(大小)

返回值
- 成功:0
- 错误:-1(错误码会被设置)

三,使用示例

1. 写入映射

#include<iostream>#include<cstdio>#include<sys/mman.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#defineFILENAME"log.txt"#defineSIZE1024intmain(){// 打开文件int fd =open(FILENAME, O_RDWR | O_APPEND | O_CREAT,0666);if(fd <0){perror("open");return1;}// 调整文件大小ftruncate(fd, SIZE);// 建立映射char* mmap_addr =(char*)mmap(nullptr, SIZE, PROT_WRITE, MAP_SHARED, fd,0);if(mmap_addr == MAP_FAILED){perror("mmap");return2;}// 写入操作for(int c ='a', i =0; c <='z'; c++, i++){ mmap_addr[i]= c;}// 取消映射munmap(mmap_addr, SIZE);// 关闭文件close(fd); std::cout <<"写入映射完毕"<< std::endl;return0;}

运行结果:

在这里插入图片描述

2. 读取映射

#include<iostream>#include<cstdio>#include<sys/mman.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#defineFILENAME"log.txt"intmain(){// 打开文件int fd =open(FILENAME, O_RDONLY);if(fd <0){perror("open");return1;}structstat st;// struct stat 类型的结构体用于记录文件的属性fstat(fd,&st);// 获得fd对应的文件的结构体// 建立映射char* mmap_addr =(char*)mmap(nullptr, st.st_size, PROT_READ, MAP_SHARED, fd,0);if(mmap_addr == MAP_FAILED){perror("mmap");return2;}// 读取操作 std::cout << mmap_addr << std::endl;// 取消映射munmap(mmap_addr, st.st_size);// 关闭文件close(fd); std::cout <<"读取映射完毕"<< std::endl;return0;}

运行效果:

在这里插入图片描述

3. 简单模拟实现malloc

在malloc里,对应大块的内存通常是使用mmap来分配的,而对应小块的内存,是用brk来分配的

这里要再介绍一个flags选项:MAP_ANONYMOUS

  • MAP_ANONYMOUS:指定要创建⼀个匿名内存映射
  • 当使使用MAP_ANONYMOUS 标志时, mmap 会分配⼀段不与任何⽂件相关联的内存区域(即这段内存没有⽂件作为其后端存储)。
  • 这种类型的映射通常用于需要分配私有内存的场景,例如进程内部的内存分配

下面用mmap简单模拟实现一下malloc

#include<iostream>#include<cstdio>#include<sys/mman.h>#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<unistd.h>#defineSIZE1024void*MyMalloc(int size){// 建立映射(匿名映射)void* mmap_addr =mmap(nullptr, size, PROT_WRITE | PROT_READ, MAP_SHARED | MAP_ANONYMOUS,-1,0);if(mmap_addr == MAP_FAILED){perror("mmap");exit(EXIT_FAILURE);}return mmap_addr;}voidMyfree(void* mmap_addr,int size){if(munmap(mmap_addr, size)==-1){perror("munmap");exit(EXIT_FAILURE);}}intmain(){char* ptr =(char*)MyMalloc(SIZE);// 写入操作for(int c ='a', i =0; c <='z'; c++, i++){ ptr[i]= c;} std::cout <<"写入后地址内容是:"<< ptr << std::endl;Myfree(ptr, SIZE);return0;}

运行效果:


🌈我的分享也就到此结束啦🌈
要是我的分享也能对你的学习起到帮助,那简直是太酷啦!
若有不足,还请大家多多指正,我们一起学习交流!
📢公主,王子:点赞👍→收藏⭐→关注🔍
感谢大家的观看和支持!祝大家都能得偿所愿,天天开心!!!

Read more

基于大数据爬虫+Python+SpringBoot+Hive的网络电视剧收视率分析与可视化平台系统(源码+论文+PPT+部署文档教程等)

基于大数据爬虫+Python+SpringBoot+Hive的网络电视剧收视率分析与可视化平台系统(源码+论文+PPT+部署文档教程等)

博主介绍:ZEEKLOG毕设辅导第一人、全网粉丝50W+,ZEEKLOG特邀作者、博客专家、腾讯云社区合作讲师、ZEEKLOG新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老师/讲师/同行前辈交流✌ 技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文降重、长期答辩答疑辅导、腾讯会议一对一专业讲解辅导答辩、模拟答辩演练、和理解代码逻辑思路。 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇🏻 不然下次找不到哟 2022-2024年最全的计算机软件毕业设计选题大全:

By Ne0inhk
异步编程实战:构建高性能Python网络应用

异步编程实战:构建高性能Python网络应用

目录 摘要 1 异步编程:为什么它是现代网络应用的必然选择 1.1 同步架构的瓶颈与异步架构的优势 2 核心技术原理深度解析 2.1 asyncio事件循环:异步编程的发动机 2.2 aiohttp框架架构解析 3 异步数据库驱动实战 3.1 异步数据库连接池管理 3.2 多数据库支持与连接池优化 4 WebSocket实时通信实战 4.1 构建高性能WebSocket服务器 4.2 实时数据推送与流处理 5 企业级实战案例 5.1 构建异步API网关 6 性能优化与故障排查 6.1 性能优化实战技巧 6.2 常见故障排查指南 7 总结与展望 7.1

By Ne0inhk

【超详细】Python FastAPI 入门:写给新手的“保姆级”教程

【超详细】Python FastAPI 入门:写给新手的“保姆级”教程(2025–2026 最新版) 这篇教程的目标是: 零基础 → 能独立写出生产级别的 RESTful API 预计认真跟着做完前 80%,你大概需要 3–10 天(每天 2–4 小时)。 目录(建议按顺序阅读) 1. 为什么选择 FastAPI(而不是 Flask / Django) 2. 环境准备(最稳的几种方式) 3. 第一个 FastAPI 程序(Hello World) 4. 核心概念速览(5 分钟建立大局观) 5. 路径参数、查询参数、请求体(

By Ne0inhk
【超详细】Python FastAPI 入门:写给新手的“保姆级”教程

【超详细】Python FastAPI 入门:写给新手的“保姆级”教程

前言  作为一名大学生,最近在做 Python Web 开发时发现了一个“宝藏”框架——FastAPI。 以前学 Django 光配置就头大,学 Flask 又不知道怎么写规范。直到遇到了 FastAPI,我才体会到什么叫“写代码像呼吸一样自然”。 这篇文章不讲复杂的原理,只讲最基础、最实用的操作,带你从 0 到 1 跑通第一个 API 接口! 一、FastAPI 是什么 在 Python 的世界里,做网站后台(Web 开发)主要有三巨头: 1. Django:老大哥,功能全但笨重,像一辆重型卡车。 2. Flask:二哥,轻便灵活但插件多,像一辆自行组装的赛车。 3.

By Ne0inhk