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

`mmap` 系统调用详解

mmap 是 Unix/Linux 系统中一个强大且多用途的系统调用,用于将文件或设备映射到进程的地址空间,实现内存映射I/O。

1. 函数的概念与用途

mmap(内存映射)函数允许程序将文件或其他对象直接映射到其地址空间,这样文件内容就可以通过内存指针直接访问,而不需要使用传统的 read/write 系统调用。

主要用途:

  • 文件I/O优化:提供对文件的高效随机访问,避免频繁的 read/write 系统调用
  • 进程间通信:通过映射同一文件实现共享内存
  • 内存分配:用于实现高效的内存分配器(如 malloc
  • 程序加载:用于加载可执行文件和共享库
  • 零拷贝I/O:在某些情况下可以实现零拷贝网络传输

2. 函数的声明与出处

mmap 函数定义在 <sys/mman.h> 头文件中,是 POSIX 标准的一部分。

#include <sys/mman.h>void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);

3. 返回值的含义与取值范围

  • 成功时:返回映射区域的起始地址
  • 失败时:返回 MAP_FAILED(通常是 (void *)-1),并设置 errno 来指示错误原因

常见错误码 (errno):

  • EACCES:文件描述符不支持所请求的访问类型
  • EAGAIN:文件已被锁定,或太多内存被锁定
  • EBADF:文件描述符无效
  • EINVAL:参数无效(如长度为零、不支持的保护或标志)
  • ENFILE:已达到系统对打开文件总数的限制
  • ENODEV:文件所在文件系统不支持内存映射
  • ENOMEM:没有可用内存,或进程超出内存映射限制
  • EPERM:操作被拒绝(如尝试写入只写区域)

4. 参数的含义与取值范围

  1. void *addr

    • 含义:建议的映射起始地址(通常设为 NULL,由内核自动选择)
    • 取值范围:任何有效的地址或 NULL
  2. size_t length

    • 含义:要映射的字节数
    • 取值范围:大于 0 的值
  3. int prot

    • 含义:内存保护标志,指定访问权限
    • 常见取值(使用按位或组合):
      • PROT_NONE:页面不可访问
      • PROT_READ:页面可读
      • PROT_WRITE:页面可写
      • PROT_EXEC:页面可执行
  4. int flags

    • 含义:映射类型和选项
    • 常见取值(使用按位或组合):
      • MAP_SHARED:共享映射,修改对其他进程可见
      • MAP_PRIVATE:私有映射,修改不会写回文件
      • MAP_ANONYMOUS:不映射文件,创建匿名内存
      • MAP_FIXED:必须使用指定地址
      • MAP_LOCKED:锁定页面在内存中
  5. int fd

    • 含义:要映射的文件描述符
    • 取值范围:有效的文件描述符(对于匿名映射,设为 -1)
  6. off_t offset

    • 含义:文件中的偏移量,必须是系统页面大小的倍数
    • 取值范围:非负值,通常是页面大小的倍数

5. 函数使用案例

案例1:文件映射与读取

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>int main() {const char *filename = "example.txt";const int file_size = 1024;// 创建并写入示例文件int fd = open(filename, O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 调整文件大小if (ftruncate(fd, file_size) == -1) {perror("ftruncate");close(fd);exit(EXIT_FAILURE);}// 映射文件到内存char *mapped = mmap(NULL, file_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// 通过内存访问文件内容const char *message = "Hello, mmap!";strncpy(mapped, message, strlen(message));printf("File content via mmap: %s\n", mapped);// 取消映射并关闭文件if (munmap(mapped, file_size) == -1) {perror("munmap");}close(fd);return 0;
}

案例2:进程间共享内存

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/wait.h>
#include <unistd.h>
#include <string.h>#define SHM_SIZE 1024int main() {// 创建共享内存区域(匿名映射)char *shared_mem = mmap(NULL, SHM_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);if (shared_mem == MAP_FAILED) {perror("mmap");exit(EXIT_FAILURE);}pid_t pid = fork();if (pid == -1) {perror("fork");exit(EXIT_FAILURE);}if (pid == 0) {  // 子进程printf("Child process writing to shared memory\n");strcpy(shared_mem, "Message from child process");exit(EXIT_SUCCESS);} else {         // 父进程wait(NULL);  // 等待子进程结束printf("Parent process reading from shared memory: %s\n", shared_mem);// 清理if (munmap(shared_mem, SHM_SIZE) == -1) {perror("munmap");}}return 0;
}

案例3:高效大数据处理

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <time.h>#define FILE_SIZE (1024 * 1024 * 100)  // 100MBint main() {const char *filename = "large_file.bin";int fd = open(filename, O_RDWR | O_CREAT, 0644);if (fd == -1) {perror("open");exit(EXIT_FAILURE);}// 调整文件大小if (ftruncate(fd, FILE_SIZE) == -1) {perror("ftruncate");close(fd);exit(EXIT_FAILURE);}// 映射文件char *mapped = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);if (mapped == MAP_FAILED) {perror("mmap");close(fd);exit(EXIT_FAILURE);}// 使用内存映射进行高效处理clock_t start = clock();for (size_t i = 0; i < FILE_SIZE; i++) {mapped[i] = (char)(i % 256);  // 填充数据}clock_t end = clock();double elapsed = (double)(end - start) / CLOCKS_PER_SEC;printf("Processed %d MB in %.2f seconds (%.2f MB/s)\n", FILE_SIZE / (1024 * 1024), elapsed, (FILE_SIZE / (1024.0 * 1024.0)) / elapsed);// 清理if (munmap(mapped, FILE_SIZE) == -1) {perror("munmap");}close(fd);remove(filename);  // 删除临时文件return 0;
}

6. 编译方式与注意事项

编译命令:

gcc -o mmap_example1 mmap_example1.c
gcc -o mmap_example2 mmap_example2.c
gcc -o mmap_example3 mmap_example3.c

注意事项:

  1. 权限检查:确保对映射的文件有适当的访问权限
  2. 对齐要求offset 参数必须是系统页面大小的倍数(可用 sysconf(_SC_PAGE_SIZE) 获取)
  3. 资源清理:始终使用 munmap() 取消映射,并使用 close() 关闭文件描述符
  4. 错误处理:始终检查返回值并处理可能的错误
  5. 同步:对于共享映射,修改可能不会立即写回文件,可使用 msync() 强制同步
  6. 内存保护:注意设置适当的保护标志,避免安全漏洞
  7. 可移植性:不同系统可能有不同的页面大小和限制

7. 执行结果说明

案例1输出:

File content via mmap: Hello, mmap!

这个示例创建了一个文件,通过内存映射写入内容,然后读取并显示内容。

案例2输出:

Child process writing to shared memory
Parent process reading from shared memory: Message from child process

这个示例演示了父子进程如何通过匿名映射共享内存区域。

案例3输出:

Processed 100 MB in 0.15 seconds (666.67 MB/s)

这个示例展示了使用内存映射处理大文件的高效性,通过直接内存访问避免了频繁的系统调用。

8. 图文总结

以下是 mmap 系统调用的工作流程:

文件映射
匿名映射
应用程序调用 mmap()
内核验证参数并保留地址空间区域
映射类型?
建立文件页表项
但不立即加载所有内容
分配物理内存页并清零
返回映射区域的指针
应用程序通过指针访问内存
访问的页面已加载?
触发缺页异常
内核加载所需数据到内存
恢复应用程序执行
直接访问内存
继续执行

关键点总结:

  1. mmap 提供了一种高效的文件访问方式,避免了频繁的 read/write 系统调用
  2. 支持文件映射和匿名映射两种模式,分别用于文件I/O和进程间通信
  3. 采用惰性加载机制,只有在实际访问时才会将数据加载到内存
  4. 对于共享映射,修改可能会自动写回文件(取决于标志)
  5. 必须正确使用 munmap() 释放映射区域,避免资源泄漏
  6. 适当设置保护标志和映射选项对性能和安全性至关重要

mmap 是一个强大的系统调用,正确使用可以显著提高I/O性能,但需要仔细考虑内存管理和同步问题。

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

相关文章:

  • 设备驱动程序 day62
  • 变压器副边电流计算
  • es-toolkit 是一个现代的 JavaScript 实用库
  • 15公里图传模组:为远程飞行赋能,突破极限的无线连接新选择
  • 微服务-28.配置管理-共享配置
  • 微服务-26.网关登录校验-OpenFeign传递用户信息
  • 前端RSA加密库优缺点总结
  • 42_基于深度学习的非机动车头盔佩戴检测系统(yolo11、yolov8、yolov5+UI界面+Python项目源码+模型+标注好的数据集)
  • Python内存模型与对象系统深度解析
  • 使用Kiro智能开发PYTHON应用程序
  • 25072班8.26日数据结构作业
  • 【CFA三级笔记】资产配置:第一章 资本市场预期(宏观分析)
  • ansible的一些重要配置文件
  • 基于 LQG 控制的轨迹跟踪 —— 从原理到实践
  • 游隼可视化项目
  • python删除执行目录
  • 服装行业/服饰品牌OMS订单管理系统:全渠道零售时代的数字化中枢|商派
  • Chrome您的连接不是私密连接怎么办?试下手敲 thisisunsafe
  • Kafka 生态选型地图、最佳实践与落地清单
  • SELinux相关介绍
  • Android 属性 property 系统
  • MyBatis-Flex多表关联查询指南
  • Dify 父子模式详解:如何实现模块化与高效协作
  • 学习做动画4.回转运动
  • Docker移动安装目录的两种实现方案
  • Qwen3-Coder-30B-A3B-Instruct AWQ 量化
  • 基于51单片机的DS18B20大棚温度监控系统
  • TRUST:a thermohydraulic software package for CFD simulations,开源多物理场数值模拟平台
  • Decode Global:以合规资质筑牢全球服务的根基
  • 数据中台的下一步,是数据飞轮吗?