news 2026/2/9 15:59:19

Qt多进程(七)内存映射

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Qt多进程(七)内存映射

前言

本节我想介绍一种基于文件的内存映射(Memory-Mapped File),它是一种非常实用且跨平台友好的进程间通信(IPC)方式。虽然它在性能上略逊于原生共享内存(QSharedMemory),但在健壮性、可调试性和兼容性方面有显著优势。

一、内存映射

核心思想:
1.将一个磁盘文件的内容映射到进程的虚拟地址空间。
2.对映射区域的读写操作,直接反映到文件中(由操作系统负责同步)。
3.多个进程映射同一个文件,即可实现共享数据——这就是一种 IPC 机制。
你可以把它理解为:
“用文件作为中介的共享内存” —— 数据存在文件里,但访问像内存一样快。

二、Qt中的实现:QFile::map()

Qt 通过 QFile 类提供了跨平台的内存映射支持:
1.file.open(QFile::ReadWrite),打开文件(必须先打开)
2.file.map(offset, size),返回 uchar* 指针,指向映射区域
3.file.unmap(ptr),解除映射
4.file.resize(size),确保文件足够大(映射大小不能超过文件实际大小)

file类调用的接口就是这些,得到的uchar*指针指向的就是文件的虚拟内存区域,直接修改可以复写在文件中。打开的时候需要文件载体,这是一个实际的文件,可以命名后缀为dat文件。针对进程通信,我们也可以有一些数据格式的设计,比方说协议头等。
另外,读取完数据之后,可以按需进行清除,比方说主动清零整个映射区。这个在后续代码中有体现。

三、代码示例

新增界面测试类:

#ifndefMEMORYMAPWINDOW_H#defineMEMORYMAPWINDOW_H#include<QWidget>#include<QTextEdit>#include<QLineEdit>#include<QPushButton>#include<QVBoxLayout>#include<QHBoxLayout>#include<QTimer>#include<QDateTime>#include<QFile>#include<QDir>classMemoryMapWindow:publicQWidget{Q_OBJECTpublic:explicitMemoryMapWindow(constQString&role,QWidget*parent=nullptr);privateslots:voidonSendMessage();voidonReadMessage();voidonTimerTimeout();private:voidappendLog(constQString&msg);voidsetupMemoryMap();QString m_role;QTextEdit*m_logView;QLineEdit*m_inputEdit;QPushButton*m_sendButton;QPushButton*m_readButton;QTimer*m_timer;QString m_mapFilePath;QFile*m_mapFile;};#endif// MEMORYMAPWINDOW_H
#include"memorymapwindow.h"#include<QLabel>MemoryMapWindow::MemoryMapWindow(constQString&role,QWidget*parent):QWidget(parent),m_role(role),m_timer(nullptr),m_mapFile(nullptr),m_mapFilePath(QDir::tempPath()+"/ipc_test_mmap.dat"){setWindowTitle("Memory Map - "+role);resize(600,500);m_logView=newQTextEdit();m_logView->setReadOnly(true);m_inputEdit=newQLineEdit();m_inputEdit->setPlaceholderText("Enter message to write...");m_sendButton=newQPushButton("Write to Memory Map");m_readButton=newQPushButton("Read from Memory Map");QVBoxLayout*mainLayout=newQVBoxLayout();mainLayout->addWidget(m_logView);mainLayout->addWidget(m_inputEdit);mainLayout->addWidget(m_sendButton);mainLayout->addWidget(m_readButton);setLayout(mainLayout);connect(m_sendButton,&QPushButton::clicked,this,&MemoryMapWindow::onSendMessage);connect(m_readButton,&QPushButton::clicked,this,&MemoryMapWindow::onReadMessage);setupMemoryMap();if(m_role=="Server"){// Server periodically checks for messagesm_timer=newQTimer(this);connect(m_timer,&QTimer::timeout,this,&MemoryMapWindow::onTimerTimeout);m_timer->start(1000);// Check every second}appendLog("Memory Map "+m_role+" initialized at: "+m_mapFilePath);}voidMemoryMapWindow::setupMemoryMap(){// Ensure file exists with sufficient sizeQFilefile(m_mapFilePath);if(!file.exists()){if(file.open(QFile::WriteOnly)){file.resize(1024);// Create with 1KB sizefile.close();}}}voidMemoryMapWindow::onSendMessage(){QString msg=m_inputEdit->text();if(msg.isEmpty())return;QFilefile(m_mapFilePath);if(!file.open(QFile::ReadWrite)){appendLog("Failed to open memory map file: "+file.errorString());return;}if(file.size()<1024){file.resize(1024);}uchar*mapped=file.map(0,1024);if(!mapped){appendLog("Failed to map memory");file.close();return;}// Clear the area firstmemset(mapped,0,1024);// Write magic header and messageQByteArray data=msg.toUtf8();if(data.size()>1020)data.truncate(1020);memcpy(mapped,"IPC!",4);// Magic headermemcpy(mapped+4,data.constData(),data.size());file.unmap(mapped);file.close();appendLog("Wrote to memory map: "+msg);m_inputEdit->clear();}voidMemoryMapWindow::onReadMessage(){QFilefile(m_mapFilePath);if(!file.exists()){appendLog("Memory map file does not exist");return;}if(!file.open(QFile::ReadOnly)||file.size()<1024){appendLog("Memory map file is too small or cannot be opened");return;}uchar*mapped=file.map(0,1024);if(!mapped){appendLog("Failed to map memory for reading");file.close();return;}// Check magic headerif(memcmp(mapped,"IPC!",4)!=0){appendLog("No valid data in memory map (magic mismatch)");file.unmap(mapped);file.close();return;}// Read message starting from offset 4QByteArraydata((char*)(mapped+4),1020);intnullPos=data.indexOf('\0');if(nullPos!=-1){data.truncate(nullPos);}file.unmap(mapped);file.close();appendLog("Read from memory map: "+QString::fromUtf8(data));}voidMemoryMapWindow::onTimerTimeout(){QFilefile(m_mapFilePath);if(!file.exists()||file.size()<1024)return;uchar*mapped=file.map(0,1024);if(!mapped)return;// Check magic headerif(memcmp(mapped,"IPC!",4)==0){// Read message starting from offset 4QByteArraydata((char*)(mapped+4),1020);intnullPos=data.indexOf('\0');if(nullPos!=-1){data.truncate(nullPos);}// Only report if there's actual contentif(!data.isEmpty()){appendLog("Server received: "+QString::fromUtf8(data));// Clear the message after readingmemset(mapped,0,1024);}}file.unmap(mapped);file.close();}voidMemoryMapWindow::appendLog(constQString&msg){m_logView->append(QDateTime::currentDateTime().toString("hh:mm:ss")+" | "+msg);}

运行效果:

四、总结

可以看到,本质上这种方式就是通过一个文件作为媒介,得到一个多进程都可以直接操作的内存块指针。使用上其实需要注意,比方说文件是否存在,通信过程中文件是否会被删除。而且涉及到多端使用会产生竟态的问题,可以像上一节一样使用信号量来管理写入读取的时机。
内存映射这种方法,和共享内存还是有点像的,以下进行一些对比总结:

现代操作系统会将频繁访问的映射文件缓存在内存(Page Cache)中,所以实际 I/O 很少,性能接近共享内存,尤其在本地 SSD 上几乎无感。
优点:
简单可靠:无需处理共享内存的“创建/附加”复杂逻辑。
天然持久化:进程崩溃后,数据仍在文件中(可用于恢复)。
易于调试:直接 cat /tmp/ipc_test_mmap.dat 查看内容。
跨语言兼容:任何能读写文件+内存映射的语言(Python/C#/Rust)都能互通。
无内核资源泄漏风险:文件由文件系统管理,重启自动清理。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/2/6 1:54:29

AI创作工作室必备:批量运行HeyGem提升产能十倍

AI创作工作室必备&#xff1a;批量运行HeyGem提升产能十倍 在短视频日活破亿、知识付费持续升温的今天&#xff0c;内容创作者正面临一个两难困境&#xff1a;用户对高质量视频的需求越来越高&#xff0c;而制作成本和时间投入却难以承受。尤其是教育机构、MCN公司和企业宣传部…

作者头像 李华
网站建设 2026/2/5 17:24:48

跨平台应用权限设计,如何实现C#中安全可靠的权限继承?

第一章&#xff1a;跨平台应用权限设计的核心挑战在构建跨平台应用时&#xff0c;权限管理成为影响用户体验与安全性的关键环节。不同操作系统&#xff08;如 iOS、Android、Windows、macOS&#xff09;对权限的定义、请求时机和用户授权机制存在显著差异&#xff0c;这使得开发…

作者头像 李华
网站建设 2026/2/7 20:34:39

SSD固态硬盘强烈推荐:加快HeyGem读写视频文件速度

SSD固态硬盘强烈推荐&#xff1a;加快HeyGem读写视频文件速度 在AI内容生成日益普及的今天&#xff0c;数字人视频合成系统正快速渗透进企业宣传、在线教育和智能客服等领域。HeyGem 作为一款基于音频驱动口型同步技术的数字人视频生成平台&#xff0c;能够将一段语音与目标人脸…

作者头像 李华
网站建设 2026/2/7 19:39:56

单个处理 vs 批量处理:HeyGem数字人系统的两种模式对比

单个处理 vs 批量处理&#xff1a;HeyGem数字人系统的两种模式对比 在AI内容生成正从“能用”迈向“好用、快用”的今天&#xff0c;一个看似简单的问题却频繁出现在数字人项目现场&#xff1a;为什么我生成一条视频只要5分钟&#xff0c;而生成10条却花了40分钟&#xff1f; 这…

作者头像 李华
网站建设 2026/2/5 14:21:42

错过将后悔!C# 12顶级语句部署必须掌握的6项核心技术

第一章&#xff1a;C# 12顶级语句概述与部署意义C# 12 引入的顶级语句&#xff08;Top-level Statements&#xff09;进一步简化了程序入口点的编写方式&#xff0c;使开发者能够以更简洁、直观的方式构建应用程序。这一特性不仅降低了新手入门门槛&#xff0c;也提升了代码的可…

作者头像 李华