【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

Spring Cloud 熔断降级详解:用 “保险丝“ 类比,Sentinel 实战教程

Spring Cloud 熔断降级详解:用 “保险丝“ 类比,Sentinel 实战教程

欢迎文末添加好友交流,共同进步! “ 俺はモンキー・D・ルフィ。海贼王になる男だ!” * 📋 目录 * 什么是熔断降级 * 定义 * 为什么需要熔断降级? * 保险丝类比:形象理解熔断机制 * 生活中的保险丝 * 熔断器工作原理对比 * 熔断器三种状态 * Sentinel 核心概念 * 什么是 Sentinel? * 核心概念对比 * Sentinel vs Hystrix 对比 * Sentinel 实战教程 * 环境准备 * 1. 添加依赖 * 2. 配置文件 * 基础示例:注解方式 * 3. 主启动类 * 4. 创建订单服务 * 5. 控制器 * 高级配置:规则定义 * 6. 流控规则配置 * OpenFeign 集成 * 7. Feign客户端集成Sentinel * 8. Feign降级处理 * 规则持久化(

By Ne0inhk
快学快用系列:一文学会java后端WebApi开发

快学快用系列:一文学会java后端WebApi开发

文章目录 * 第一部分:Web API开发基础概念 * 1.1 什么是Web API * 1.2 RESTful API设计原则 * 第二部分:开发环境搭建 * 2.1 环境要求 * 2.2 创建Spring Boot项目 * 2.3 配置文件 * 第三部分:项目架构设计 * 3.1 分层架构 * 3.2 包结构设计 * 第四部分:数据模型设计 * 4.1 实体类设计 * 4.2 DTO设计 * 第五部分:数据访问层实现 * 5.1 Repository接口 * 5.2 自定义Repository实现 * 第六部分:业务逻辑层实现

By Ne0inhk
从 0 到 1 玩转前端加密 encrypt-labs 靶场:环境搭建 + 全关卡解析

从 0 到 1 玩转前端加密 encrypt-labs 靶场:环境搭建 + 全关卡解析

文章目录 * 前言 * 1 环境搭建(Docker 混淆版) * 2 配置插件 * 2.1 Galaxy * 2.2 autoDecoder * 3 AES固定Key * 4 AES服务端获取Key * 5 Rsa加密 * Galaxy * autoDecoder * 6 AES+Rsa加密 * Galaxy * autoDecoder * 7 DES规律key * Galaxy * autoDecoder * 8 明文加签 * 9 加签key在服务器端 * 10 禁止重放 ⚠️本博文所涉安全渗透测试技术、方法及案例,仅用于网络安全技术研究与合规性交流,旨在提升读者的安全防护意识与技术能力。任何个人或组织在使用相关内容前,必须获得目标网络 / 系统所有者的明确且书面授权,严禁用于未经授权的网络探测、漏洞利用、数据获取等非法行为。 前言 在 Web

By Ne0inhk
AI对话页的流式处理架构:基于Web Streams+Fetch API的实践

AI对话页的流式处理架构:基于Web Streams+Fetch API的实践

引言         当前AI浪潮下,基于各大agent平台,我们可以在几分钟内就搭建出一个具备页面交互的智能体,从问答输出到页面交互,这个过程中的数据流转、UI实现被统一封装以降低模型搭建复杂度。为了探索这个过程的底层实现,我们采用“生产者-消费者模式”的流式处理架构,将网络IO、数据解码、文本解析与UI渲染解耦,实现实时流式响应、UI增量渲染。 使用框架如下: * 前端框架:Vue 3 + TypeScript + Vite * UI组件库:Ant Design Vue、Ant Design X Vue * 流处理:Web Streams API + Fetch API         从请求发送到UI渲染,流程如下: 流式响应处理 请求管理 1. 采用 AbortController、ReadableStreamDefaultReader 实现“上游网络请求中止”和“下游字节流读取控制”,共同实现一次会话的可取消、可停止的流式处理。

By Ne0inhk