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

linux开发之mmap内存映射

mmap概念

mmp是 将文件或设备直接映射到进程的虚拟内存空间 的一种机制,可实现程序像访问内存一样访问文件,而不需要传统的 read()/write()系统调用

文件内容被映射到进程的地址空间,读写文件就像操作内存一样,操作系统负责自动同步内存和文件的修改(除非显式禁用)。
好处:

  • 文件 I/O 优化:避免频繁的 read()/write(),提升性能,例如数据库引擎(如 SQLite、LevelDB)用 mmap加速磁盘访问
  • 多个进程需要高效共享数据(如父子进程、独立进程),比管道、消息队列更快(直接内存访问),Chrome 浏览器用 mmap共享多个标签页的内存,机器学习训练时,多个进程共享模型参数
  • 序运行时加载动态链接库(.so/.dll)时,库文件被映射到内存,按需加载(懒加载),节省内存
  • sendfile:文件通过 mmap映射到内存,直接通过 DMA 发送到网卡,无需 CPU 参与拷贝

语法:函数声明

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
参数说明
addr建议映射的起始地址(通常传 NULL,由系统自动分配)
length映射区域的长度(字节)
prot内存保护方式(PROT_READ, PROT_WRITE 等)
flags映射类型(MAP_SHARED, MAP_PRIVATE
fd文件描述符(通过 open 获得)
offset文件映射起始偏移(必须是页大小的整数倍)

相关常量:

// 保护方式
PROT_READ      // 可读
PROT_WRITE     // 可写
PROT_EXEC      // 可执行
PROT_NONE      // 不可访问// 映射类型
MAP_SHARED     // 修改会写回文件,进程间共享
MAP_PRIVATE    // 写时复制,修改不写回文件// 取消映射
int munmap(void *addr, size_t length);  // 释放映射

mmap写文件

[root@prs31 01-fd]# cat 01-mmap_read.c 
// 01-mmap_read.c#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>int main() {int fd;struct stat sb;char *mapped;fd = open("test.txt", O_RDONLY);if (fd == -1) {perror("open");exit(1);}// 2. 获取文件状态(主要是大小)if (fstat(fd, &sb) == -1) {perror("fstat");exit(1);}// 3. 映射文件到内存mapped = mmap(NULL, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(1);}// 4. 像访问数组一样读取内容printf("文件内容:\n");for (off_t i = 0; i < sb.st_size; i++) {putchar(mapped[i]);     // 逐字符打印内存映射文件(mmap)的内容}// 5. 解除映射if (munmap(mapped, sb.st_size) == -1) {perror("munmap");}close(fd);return 0;
}

在这里插入图片描述# mmap追加文件内容

[root@prs31 01-fd]# cat 02-mmap_write.c 
// mmap_write.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>int main() {int fd;struct stat sb;char *mapped;const char *new_content = "\nAppended via mmap!";// 1. 以读写方式打开文件fd = open("test.txt", O_RDWR);if (fd == -1) {perror("open");exit(1);}// 2. 获取文件大小if (fstat(fd, &sb) == -1) {perror("fstat");exit(1);}// 3. 扩展文件大小(为追加内容预留空间)off_t new_size = sb.st_size + strlen(new_content);if (ftruncate(fd, new_size) == -1) {perror("ftruncate");close(fd);exit(1);}// 4. 映射整个新大小的文件,这里传递使用扩展后的文件大小mapped = mmap(NULL, new_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(1);}// 5. 在原内容末尾写入新内容,strcpy(目的变量,要添加的变量)strcpy(mapped + sb.st_size, new_content);printf("已追加内容到文件\n");if (munmap(mapped, new_size) == -1) {perror("munmap");}close(fd);return 0;
}

在这里插入图片描述# mmap实现进程间共享内存

[root@prs31 01-fd]# cat 03-shm_writer.c 
// 03-shm_writer.c
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <string.h>int main() {int fd;char *mapped;// 1. 创建共享文件fd = open("/tmp/shm_file", O_CREAT | O_RDWR, 0644);if (fd == -1) {perror("open");exit(1);}// 2. 扩展文件大小ftruncate(fd, 4096);  // 1页// 3. 映射mapped = mmap(NULL, 4096, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");exit(1);}// 4. 写入数据sprintf(mapped, "Hello from Process A (PID: %d)", getpid());printf("写入: %s\n", mapped);printf("等待按回车继续...\n");getchar();  // 等待进程B读取munmap(mapped, 4096);close(fd);return 0;
}

[root@prs31 01-fd]# cat 03-shm_reader.c 
// 03-shm_writer
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/mman.h>int main() {int fd;char *mapped;fd = open("/tmp/shm_file", O_RDONLY);if (fd == -1) {perror("open");exit(1);}mapped = mmap(NULL, 4096, PROT_READ, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");exit(1);}printf("读取到: %s\n", mapped);munmap(mapped, 4096);close(fd);return 0;
}

运行代码

[root@prs31 01-fd]# gcc 03-shm_writer.c shm_writer
[root@prs31 01-fd]# gcc 03-shm_reader.c shm_reader

在这里插入图片描述

第二个终端:
在这里插入图片描述

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

相关文章:

  • 算法解决爬楼梯问题
  • SQL注入攻击基础
  • 【LVGL自学笔记暂存】
  • 如何正确选择建站工具?
  • FPGA高端项目:图像采集+Aurora 8B10B+UDP图传架构,基于GTP高速收发器的光口转网口,提供4套工程源码和技术支持
  • 旧物回收小程序系统开发:连接你我,共筑环保梦想
  • Linux下动态库链接的详细过程
  • 【网络运维】Linux:NFS服务器原理及配置
  • Kafka数据生产和发送
  • RuoYi OpenAPI集成从单体到微服务改造全过程记录
  • 高速公路安装定向广播的优势
  • centos VMware ESXi 扩容
  • 为什么任务顺序会影响效率?如何实现自定义顺序?
  • Python 基础详解:数据类型(Data Types)—— 程序的“数据基石”
  • Fiddler 安装配置教程
  • 认识汇编:解码计算机思维的底层语言(第一章)
  • 【YOLO学习笔记】YOLOv8详解解读
  • WEB开发-第二十七天(PHP篇)
  • 【Unity Plugins】使用ULipSync插件实现人物唇形模拟
  • 基于Spring Cloud Stream与Kafka的事件驱动微服务架构设计与实战指南
  • 【Python】基于Python自动化邮件发送系统:从配置到实现的完整指南
  • 【YOLOv8改进 - C2f融合】C2f融合SFS-Conv(空间 - 频率选择卷积)提升特征多样性,同时减少参数和计算量
  • 如何在 VS Code 中进行 `cherry-pick`
  • 使用Python验证常见的50个正则表达式
  • react接口防抖处理
  • [网格图DP]3363. 最多可收集的水果数目
  • 视频二维码如何助力博物馆打造智慧讲解体验
  • 数据库事务总结
  • 升级g++编译器
  • RK3568项目(十二)--linux驱动开发之基础通讯接口(上)