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

#Linux内存管理# 详细介绍使用mmap函数创立共享文件映射的工作原理

使用 mmap 创建共享文件映射 - 详细解析与代码示例

 

共享文件映射的工作原理

 

共享文件映射 (MAP_SHARED) 允许多个进程访问同一文件的相同内存区域,所有修改都会同步回文件,并实时对其他进程可见:

 

1.内存地址映射

 

不同进程将同一文件映射到各自虚拟地址空间的不同地址处

 

2.页面共享机制

 

所有进程共享内核中的同一份物理内存页缓存

 

3.同步更新

 

任一进程的修改会:

 

 立即反映到其他进程的内存视图

 

 由内核管理同步回磁盘文件

 

4.零拷贝优势

 

完全避免进程间数据复制

 

完整代码示例

 

#include <stdio.h>

#include <stdlib.h>

#include <fcntl.h>

#include <sys/mman.h>

#include <sys/stat.h>

#include <sys/wait.h>

#include <unistd.h>

#include <string.h>

#include <time.h>

 

#define FILENAME "shared_file.bin"

#define FILESIZE 4096 // 建议使用页大小的倍数(通常4KB)

#define DATA_SIZE 256

 

// 共享内存区域结构体

struct shared_data {

    int counter;

    char timestamp[DATA_SIZE];

    char message[DATA_SIZE];

};

 

void writer_process(void *shared_ptr) {

    struct shared_data *data = (struct shared_data *)shared_ptr;

    

    for (int i = 0; i < 5; i++) {

        // 更新计数器

        data->counter++;

        

        // 更新时间戳

        time_t now = time(NULL);

        strftime(data->timestamp, DATA_SIZE, "%Y-%m-%d %H:%M:%S", localtime(&now));

        

        // 更新消息

        snprintf(data->message, DATA_SIZE, "进程 %d 写入了第 %d 条数据", getpid(), data->counter);

        

        printf("写入进程 [%d]: 计数器=%d | %s\n", getpid(), data->counter, data->message);

        

        // 模拟处理时间

        usleep(800000); // 800ms

    }

}

 

void reader_process(void *shared_ptr) {

    struct shared_data *data = (struct shared_data *)shared_ptr;

    

    for (int i = 0; i < 10; i++) {

        printf("读取进程 [%d]: 计数器=%d | %s | 时间: %s\n", 

              getpid(), data->counter, data->message, data->timestamp);

        

        // 模拟处理时间

        usleep(500000); // 500ms

    }

}

 

int main() {

    int fd = open(FILENAME, O_RDWR | O_CREAT, 0666);

    if (fd == -1) {

        perror("文件打开失败");

        exit(EXIT_FAILURE);

    }

    

    // 设置文件大小

    if (ftruncate(fd, FILESIZE) == -1) {

        perror("文件大小设置失败");

        close(fd);

        exit(EXIT_FAILURE);

    }

    

    // 创建共享文件映射

    void *shared_ptr = mmap(NULL, FILESIZE, 

                          PROT_READ | PROT_WRITE, 

                          MAP_SHARED, 

                          fd, 0);

    

    if (shared_ptr == MAP_FAILED) {

        perror("内存映射失败");

        close(fd);

        exit(EXIT_FAILURE);

    }

    

    // 初始化共享区域

    struct shared_data *data = (struct shared_data *)shared_ptr;

    data->counter = 0;

    strcpy(data->timestamp, "未初始化");

    strcpy(data->message, "等待数据...");

    

    printf("=============================================\n");

    printf("共享文件映射创建成功!\n");

    printf("文件: %s\n", FILENAME);

    printf("映射地址: %p\n", shared_ptr);

    printf("文件大小: %d 字节\n", FILESIZE);

    printf("=============================================\n\n");

    

    // 创建两个子进程

    pid_t pid_reader = fork();

    

    if (pid_reader == 0) {

        // 第一个子进程 - 读取进程

        reader_process(shared_ptr);

        exit(EXIT_SUCCESS);

    }

    

    pid_t pid_writer = fork();

    

    if (pid_writer == 0) {

        // 第二个子进程 - 写入进程

        writer_process(shared_ptr);

        exit(EXIT_SUCCESS);

    }

    

    // 等待所有子进程退出

    wait(NULL);

    wait(NULL);

    

    // 解除映射

    if (munmap(shared_ptr, FILESIZE) == -1) {

        perror("取消映射失败");

    }

    

    close(fd);

    

    printf("\n最终结果:\n");

    printf(" 计数器: %d\n", data->counter);

    printf(" 时间戳: %s\n", data->timestamp);

    printf(" 消息: %s\n", data->message);

    printf(" 结果已保存到文件 '%s'\n", FILENAME);

    

    return 0;

}

 

关键代码解析

 

1. 创建共享文件映射

 

void *shared_ptr = mmap(NULL, FILESIZE, 

                      PROT_READ | PROT_WRITE, 

                      MAP_SHARED, // 关键参数:共享映射

                      fd, 0);

       

MAP_SHARED: 确保修改对其他进程和文件可见

 

PROT_READ | PROT_WRITE: 进程具有读写权限

 

文件大小设置为4096字节(系统页大小的倍数)       

       

 

2. 数据同步机制

 

// 写入进程

data->counter++;

strftime(data->timestamp, ...);

snprintf(data->message, ...);

 

// 读取进程

printf("%s", data->message); // 立即看到更新

 

写入进程的任何修改都会实时反映在读取进程中

 

所有修改最终都会自动同步回文件

 

3. 结构化数据共享

 

struct shared_data {

    int counter;

    char timestamp[DATA_SIZE];

    char message[DATA_SIZE];

};

 

定义清晰的数据结构规范共享内存格式

 

所有进程使用相同的内存布局

 

4. 文件资源管理

 

// 设置文件大小

ftruncate(fd, FILESIZE);

 

// 清理资源

munmap(shared_ptr, FILESIZE);

close(fd);

 

使用 ftruncate 预先设置文件大小

 

程序退出前取消映射并关闭文件

 

 

5. 编译运行与输出示例

 

# 编译程序

gcc shared_mmap.c -o shared_mmap

 

# 运行程序

./shared_mmap

 

示例输出:       

 

=============================================

共享文件映射创建成功!

文件: shared_file.bin

映射地址: 0x7f8d9a2a9000

文件大小: 4096 字节

=============================================

 

读取进程 [29876]: 计数器=0 | 等待数据... | 时间: 未初始化

写入进程 [29875]: 计数器=1 | 进程 29875 写入了第 1 条数据

读取进程 [29876]: 计数器=1 | 进程 29875 写入了第 1 条数据 | 时间: 2023-08-05 14:30:22

读取进程 [29876]: 计数器=1 | 进程 29875 写入了第 1 条数据 | 时间: 2023-08-05 14:30:22

写入进程 [29875]: 计数器=2 | 进程 29875 写入了第 2 条数据

读取进程 [29876]: 计数器=2 | 进程 29875 写入了第 2 条数据 | 时间: 2023-08-05 14:30:23

...(更多交替输出)...

读取进程 [29876]: 计数器=5 | 进程 29875 写入了第 5 条数据 | 时间: 2023-08-05 14:30:26

 

最终结果:

  计数器: 5

  时间戳: 2023-08-05 14:30:26

  消息: 进程 29875 写入了第 5 条数据

  结果已保存到文件 'shared_file.bin'

  

  

  

优势与应用场景

 

1.高性能进程通信

 

比管道、套接字等传统IPC快10-100倍

 

避免数据复制开销  

 

2.实时数据共享

 

进程间零延迟数据访问

 

适用于高频交易、实时监控系统

 

3.大型文件处理

 

高效处理超大文件(GB~TB级)

 

使用 mmap 比 read/write 节省CPU资源

 

4.内存数据库

 

通过文件映射实现持久化

 

故障后数据不丢失

 

 

注意事项

 

1.内存对齐

 

// 获取系统页大小

long page_size = sysconf(_SC_PAGESIZE);

 

2.同步机制

 

对共享数据的复杂操作需添加互斥锁

 

使用 POSIX 信号量或互斥锁

 

3.边界安全

 

避免访问超出映射区域的内存

 

错误示例:data->message[DATA_SIZE] = 'x';

 

4.文件管理

 

 修改大小后需重新映射

 

 使用 msync() 强制同步到磁盘

 

此代码展示了 mmap 共享文件映射的核心机制,实际应用时可结合业务需求进行扩展和优化。

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

相关文章:

  • 篇四 tcp,udp客户端服务器编程模型
  • [Linux入门] Linux 文件系统与日志分析入门指南
  • 欢迎咨询年度规划2025
  • 第二阶段-第二章—8天Python从入门到精通【itheima】-136节(Python操作MySQL的基础使用)
  • C++ 多态全解析:静态多态与动态多态详解
  • Packmol聚合物通道模型建模方法
  • OpenCV 图像预处理:颜色操作与灰度、二值化处理详解
  • 最长递增子序列(LIS)问题详解
  • 0723 单项链表
  • FreeRTOS学习笔记之调度机制
  • MySQL 8.0 OCP 1Z0-908 题目解析(34)
  • 打造你的AI助手:Sim Studio 开源工作流构建工具
  • 鸿蒙应用开发:使用Navigation组件和Tab组件实现首页tab选项卡及子页跳转功能
  • 第一次实习经历
  • Java——Spring中Bean配置核心规则:id、name、ref的用法与区别
  • freqtrade在docker运行一个dryrun实例
  • 内容梳理|新手体会大模型AI接口调用
  • EDoF-ToF: extended depth of field time-of-flight imaging解读, OE 2021
  • 《WebGL打造高性能3D粒子特效系统:从0到1的技术探秘》
  • AR维修辅助系统UI设计:虚实融合界面中的故障标注与操作引导
  • nginx.conf配置文件以及指令详解
  • 暑期自学嵌入式——Day06(C语言阶段)
  • 红松推出国内首个银发AI播客产品,首创“边听边问”交互体验
  • 5.综合案例 案例演示
  • [硬件电路-76]:无论是波长还是时间,还是能量维度来看,频率越高,越走进微观世界,微观世界的影响越大;频率越低,越走进宏观世界,微观世界的影响越小;
  • 销采一体化客户管理系统核心要点速通
  • IDEA202403 超好用设置【持续更新】
  • SAP第二季度利润大增但云业务疲软,股价承压下跌
  • 【笔记】Handy Multi-Agent Tutorial 第三章: CAMEL框架简介及实践(实践部分)
  • HCIP笔记(第一、二章)