逆序存储文件(三)——文件内存映射

PCIE配置空间探秘:从BAR寄存器到内存映射实战 本文深入解析PCIE配置空间中的BAR寄存器及其在内存映射中的关键作用,通过实战案例展示32位与64位地址空间的配置差异与优化技巧。文章详细介绍了BAR寄存器的工作原理、配置过程及常见问题排查方法,帮助开发者高效管理PCIE设备内存空间,提升系统性能。 阅读详情

        之前介绍的两种逆序存储文件的方法,由于性能问题,只适用于小文件的操作,如果源文件较大,几M,几百M,甚至几G,则需要使用文件内存映射的方式。我对内存映射的原理认识不深,也未做研究,我只知道,使用内存映射将文件映射到内存后,就可以直接通过操作映射的内存块来操作磁盘中的文件,避免了我们主动调用磁盘I/O(I/O过程被操作系统接管),从而提高了文件读写性能。

        文件内存映射的过程是:创建文件句柄(CreateFile)->创建文件映射对象句柄(CreateFileMapping)->将文件映射到进程地址空间(MapViewOfFile)->操作->解除映射内存(UnmapViewOfFile)->关闭资源句柄(CloseHandle),实例代码如下(逆序存储):

 

//FileMapping.h
#pragma once
#include <Windows.h>
#include <tchar.h>


class FileMapping
{
public:
	FileMapping();
	~FileMapping();

	void SetSrcFile(TCHAR* pSrcFile);
	void SetDestFile(TCHAR* pDestFile);

	bool DoTransfer();
private:
	bool InitSrcFileMapping();
	bool InitDestFileMapping();
	void UninitSrcFileMapping();
	void UninitDestFileMapping();
	bool ReverseCopy()const;
	bool IsEnCharacter(const char* pCh)const
	{
		if(!pCh) return false;
		return !(*pCh & 0x00000080);
	}

	TCHAR* m_pSrcFile;
	TCHAR* m_pDestFile;

	bool m_bInitSrc;
	HANDLE m_hSrcFile;
	HANDLE m_hSrcFileMapping;
	char* m_pStartSrcBlock;
	DWORD m_dwSrcBlock;

	bool m_bInitDest;
	HANDLE m_hDestFile;
	HANDLE m_hDestFileMapping;
	char* m_pStartDestBlock;

};

 

//FileMapping.cpp

#include "FileMapping.h"
#include <cassert>
#include <malloc.h>

FileMapping::FileMapping()
	: m_pSrcFile(NULL)
	, m_bInitSrc(false)
	, m_hSrcFile(NULL)
	, m_hSrcFileMapping(NULL)
	, m_pStartSrcBlock(NULL)
	, m_dwSrcBlock(0)
	, m_pDestFile(NULL)
	, m_bInitDest(false)
	, m_hDestFile(NULL)
	, m_hDestFileMapping(NULL)
	, m_pStartDestBlock(NULL)
{

}

FileMapping::~FileMapping()
{
	UninitDestFileMapping();
	UninitSrcFileMapping();
}

void FileMapping::SetSrcFile(TCHAR* pSrcFile)
{
	m_pSrcFile = pSrcFile;
}


void FileMapping::SetDestFile(TCHAR* pDestFile)
{
	m_pDestFile = pDestFile;
}

bool FileMapping::DoTransfer()
{
	bool bInit = InitSrcFileMapping();
	if(!bInit)
		return false;

	bInit = InitDestFileMapping();
	if(!bInit)
	{
		UninitSrcFileMapping();
		return false;
	}
	
	ReverseCopy();
	UninitDestFileMapping();
	UninitSrcFileMapping();
	return true;
}


bool FileMapping::ReverseCopy()const
{
	char* pSrc = m_pStartSrcBlock + m_dwSrcBlock;
	char* pDest = m_pStartDestBlock;

	--pSrc;
	assert(pSrc >= m_pStartSrcBlock);
	while(pSrc >= m_pStartSrcBlock)
	{
		if(IsEnCharacter(pSrc))//考虑中文字符和英文字符,根据ANSI编码的文本文件中中文字符为两个字节,高字节最高位为1判断
			*pDest++ = *pSrc--;
		else
		{
			assert(pSrc >= m_pStartSrcBlock);
			*pDest++ = *(--pSrc);
			*pDest++ = *(pSrc + 1);
			--pSrc;
		}
	}
	return true;
}


bool FileMapping::InitSrcFileMapping()
{
	assert(!m_bInitSrc);
	if(m_bInitSrc) return true;

	assert(m_pSrcFile);
	if(!m_pSrcFile) return false;

	//open file, notify os the file that will be mapping to memory
	m_hSrcFile = CreateFile(m_pSrcFile, 
		GENERIC_READ,
		FILE_SHARE_READ,
		NULL,
		OPEN_EXISTING,
		FILE_FLAG_RANDOM_ACCESS,
		NULL);
	if(m_hSrcFile == INVALID_HANDLE_VALUE) return false;


	//create a file mapping object, notify os size of file and access mode
	m_hSrcFileMapping = CreateFileMapping(m_hSrcFile,
		NULL, PAGE_READONLY, 0, 0, NULL);

	DWORD dwError = GetLastError();

	if(!m_hSrcFileMapping
		|| dwError == ERROR_ALREADY_EXISTS)
	{
		CloseHandle(m_hSrcFile);
		m_hSrcFile = NULL;
		return false;
	}


	//mapping file to process address
	//_int64 qwFileSize = 0x40000000;
	_int64 qwFileOffset = 0;
	m_pStartSrcBlock = (char*)MapViewOfFile(m_hSrcFileMapping,
		FILE_MAP_READ, 
		(DWORD)(qwFileOffset>>32), 
		(DWORD)(qwFileOffset&0xFFFFFFFF), 
		0);


	if(!m_pStartSrcBlock)
	{
		CloseHandle(m_hSrcFileMapping);
		CloseHandle(m_hSrcFile);
		return false;
	}

	DWORD dwSize = 0;
	m_dwSrcBlock = GetFileSize(m_hSrcFile, &dwSize);//save size of source file, use to create mapping of target file

	m_bInitSrc = true;
	return true;
}

void FileMapping::UninitSrcFileMapping()
{
	//assert(m_bInitSrc);
	if(!m_bInitSrc) return;
	//unmapping
	if(m_pStartSrcBlock)
	{
		UnmapViewOfFile(m_pStartSrcBlock);
		m_pStartSrcBlock = NULL;
	}
	//close handle
	if(m_hSrcFileMapping)
	{
		CloseHandle(m_hSrcFileMapping);
		m_hSrcFileMapping = NULL;
	}
	if(m_hSrcFile)
	{
		CloseHandle(m_hSrcFile);
		m_hSrcFile = NULL;
	}

	m_bInitSrc = false;
}


void FileMapping::UninitDestFileMapping()
{
	//assert(m_bInitDest);
	if(!m_bInitDest) return;
	//unmapping
	if(m_pStartDestBlock)
	{
		UnmapViewOfFile(m_pStartDestBlock);
		m_pStartDestBlock = NULL;
	}
	//close handle
	if(m_hDestFileMapping)
	{
		CloseHandle(m_hDestFileMapping);
		m_hDestFileMapping = NULL;
	}
	if(m_hDestFile)
	{
		CloseHandle(m_hDestFile);
		m_hDestFile = NULL;
	}

	m_bInitDest = false;
}

bool FileMapping::InitDestFileMapping()
{
	assert(!m_bInitDest);
	if(m_bInitDest) return true;

	assert(m_pDestFile);
	if(!m_pDestFile) return false;

	m_hDestFile = CreateFile(m_pDestFile, 
		GENERIC_READ | GENERIC_WRITE,
		FILE_SHARE_READ/* | FILE_SHARE_WRITE*/,
		NULL,
		CREATE_ALWAYS,
		FILE_FLAG_SEQUENTIAL_SCAN,
		NULL);
	if(m_hDestFile == INVALID_HANDLE_VALUE) return false;


	//create a file mapping object, notify os size of file and access mode
	m_hDestFileMapping = CreateFileMapping(m_hDestFile,
		NULL, 
		PAGE_READWRITE, 
		0,
		m_dwSrcBlock,
		NULL);

	DWORD dwError = GetLastError();

	if(!m_hDestFileMapping
		|| dwError == ERROR_ALREADY_EXISTS)
	{
		CloseHandle(m_hDestFile);
		m_hDestFile = NULL;
		return false;
	}


	//mapping file to process address
	//_int64 qwFileSize = 0x40000000;
	_int64 qwFileOffset = 0;
	m_pStartDestBlock = (char*)MapViewOfFile(m_hDestFileMapping,
		FILE_MAP_READ | FILE_MAP_WRITE, 
		(DWORD)0, 
		(DWORD)(qwFileOffset&0xFFFFFFFF), //0
		0);

	if(!m_pStartDestBlock)
	{
		CloseHandle(m_hDestFileMapping);
		CloseHandle(m_hDestFile);
		return false;
	}

	m_bInitDest = true;
	return true;
}


 

#include "FileMapping.h"
#include <iostream>
#include <ctime>
using namespace std;

int main()
{
	time_t tBegin = time(NULL);
	FileMapping fm;
	fm.SetSrcFile(_T("in1.txt"));//设置源文件
	fm.SetDestFile(_T("out1.txt"));//设置目标文件
	fm.DoTransfer();//将源文件逆序存储到目标文件
	time_t tEnd = time(NULL);
	cout<<"file mapping reverse storage consume time = "<<tEnd - tBegin<<endl;
	return 0;
}


使用了三种方式对一个大小为17M的文本文件进行逆序存储,采用内存映射方式的性能远好于另外两种,下面输出了处理的时间(单位s)

文件指针定位和使用stl容器(结果不正确,没有处理中文字符),都是直接操作磁盘

结果

内存映射:

结果

 

ps:对内存映射的原理不是很清楚,需要加强学习,另外文件映射的策略要根据具体的需求,这边我一次性将整个文件映射进内存,如果文件超过几十兆,就需要改变映射的策略,每次只取一部分映射,下次映射的偏移为上一次映射的大小。

告别Redis?用C++手把手教你玩转LMDB:一个嵌入式内存映射数据库的实战入门 本文详细介绍了如何从Redis迁移到LMDB,一个高性能嵌入式内存映射数据库。通过C++实战指南,展示了LMDB的零拷贝访问、写时复制和崩溃安全等核心优势,帮助开发者在数据密集型应用中实现内存速度与持久化的完美平衡。 阅读详情

相关推荐

ESP32双核架构与内存映射深度解析:哈佛结构、总线隔离与DMA协同

嵌入式系统中的内存架构是性能与可靠性的底层基石。ESP32采用同构双核(PRO_CPU/APP_CPU)与混合哈佛结构,通过指令/数据总线物理分离实现并行吞吐与NX安全保护,同时在地址空间上划分为纯指令域、纯数据域及共用域,支撑低功耗RTC驻留与缓存复用等关键场景。其内存映射存在字序逆序、CPU访问权限不对称、外设双映射等工程偏差,直接影响DMA传输对齐、Cache一致性及中断响应确定性。结合片上SRAM分块约束、ROM重映射机制与PSRAM时序校准,开发者需在编译期链接策略、运行期同步屏障(memw/me

weixin_29317963的博客 48

逆序存储【数据结构】

C语言中malloc是动态内存分配函数。 函数原型:void malloc(unsigned int num_bytes); 参数:num_bytes 是无符号整型,用于表示分配的字节数。 返回值:如果分配成功则返回指向被分配内存的指针(此存储区中的初始值不确定),否则返回空指针NULL。void 表示未确定类型的指针,void 可以指向任何类型的数据,更明确的说是指申请内存空间时还不知道用户是用这段空间来存储什么类型的数据(比如是char还是int或者…) 功能:分配长度为num_bytes字节的内存块

qq_46474413的博客 3106

C语言数组逆序操作:利用指针高效实现

指针是一种变量,其值为另一个变量的地址。指针的初始化需要指定它将指向哪种数据类型的内存地址。例如:// pNumber 指向 number 的地址这里的pNumber是一个整型指针,&number是取number变量的地址。一维数组是最基本的数据结构之一,它是一种线性表结构,可以存储同类型的数据元素。在内存中,一维数组按照连续的内存空间顺序存储,每个数组元素占据相同的内存大小。数组中的每个元素都可以通过下标访问,而下标通常从0开始。

weixin_35756690的博客 839

字符串的逆序存储

##字符串的逆序存储 这两天在学习过程中看到一个关于字符串的逆序存储问题,在思考之后想到了该如何去实现,在下做一简单的描述。 字符串的逆序存储,具体来说可以将整个字符串放在一个数组之中,然后进行数组里面的位置互换: Data =A.Str [i]; A.Str [i]=A.Str [j]; A.Str [j]=Data ; 运行如图(逆序存储了第一位和第五位): 完整的代码如下: #include <iostream> using namespace std; #define m 5

A_daze的博客 2497

内存映射的使用——实现大文件内容倒序

将大文件通过文件映射的方式进行逆序!非常好的方法!

STL文件的读取(普通文件解析与内存映射文件解析) cpp&stl

本实例中定义了STL文件中的基本数据结构,采用普通文件读取和内存映射文件读取两种方式,并对二者效率进行对比

数组逆序存储

描述 将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。 关于输入 输入为两行:第一行数组中元素的个数n(1 < n < 100),第二行是n个整数,每两个整数之间用空格分隔。 关于输出 输出为一行:输出逆序后数组的整数,每两个整数之间用空格分隔。 例子输入 5 8 6 5 4 1 例子输出 1 4 5 6 8 源码实现 #include<iostream> #include<algorithm> #include<v

Hyx1273724818的博客 3455

实验四 基于IDEA+SpringBoot+Maven+Thymeleaf的系统实现实验目的

通过这次实验,我学习了如何成功进行IDEA+SpringBoot+maven的系统实现,搭建好IDEA+SpringBoot+maven的系统环境,成功地安装并配置了maven的运行环境,学会了创建springboot的三种方式,并且能将之前的实验结合起来运用。pom.xml中引入thymeleaf依赖,在classpath:/templates/下新建HTML页面(可采用实验一创建的页面),新建controller进行测试,访问页面后返回success成功数据。同时掌握和thymeleaf的基本语法。

m0_73502929的博客 1380

C:数据在内存中的存储

正数在内存中的存储,浮点数在内存中的存储,大小端字节序

JonlyMay的博客 3267

从帧结构到数据解析:深入理解CJ/T 188 MBUS水表通信协议

本文深入解析CJ/T 188 MBUS水表通信协议,从帧结构到数据解析提供完整指南。通过典型报文示例和实战案例,详细讲解地址域、控制码、数据标识符(DI)等关键字段的解析技巧,并分享校验算法与常见错误排查方法,帮助开发者快速掌握MBUS协议在水表通信中的应用。

weixin_28710999的博客 423

SAP S/4 HANA CO 成本模块实战指南

摘要:SAP S/4HANA CO(管理会计)模块是企业内部成本管理的核心工具。本文系统介绍了CO模块的组织结构(控制范围与会计科目表映射)、基础概念(计划值与预算值)以及6大子模块功能(成本要素、成本中心会计等)。重点解析了各子模块的应用场景与差异,包括成本归集、费用分摊、利润分析和产品成本控制,并对比了利润中心与成本中心、内部订单与销售订单的区别。通过事务代码示例和功能对比表,帮助用户快速掌握SAP CO模块的核心管理逻辑与实施要点。

金牌架构师 1125

数组进行逆序数存储,可随意输入数组,不限定数组长度

import java.util.Scanner; public class ke9_18 { public static void main(String[] args) { int i, j; Scanner reader = new Scanner(System.in); System.out.println("请输入你进行逆反操作的数"); String input = reader.nextLine(); i.

王陈锋的博客 747

用 CORRESPONDING #( ) 优雅复制同名字段:从 MOVE-CORRESPONDING 到现代 ABAP 结构映射的关键差异与最佳实践

摘要:本文深入探讨了SAP ABAP中结构数据复制的两种主要方式——传统MOVE-CORRESPONDING与现代CORRESPONDING #()的核心差异。重点分析了现代构造表达式会初始化目标结构的关键特性,通过SAP_GWFND开发中的实际案例展示了这一差异可能引发的线上问题。同时介绍了使用BASE保留目标字段值的技巧,以及通过MAPPING处理字段名不一致的场景。文章为ABAP开发者提供了在S/4HANA、BTP及RAP等现代架构下的数据映射最佳实践,帮助规避常见的维护陷阱。(149字)

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 2262

Windows核心编程—第17章—内存映射文件

当exe文件被映射到进程的地址空间中之后,系统将访问exe文件的一个部分,该部分列出了包含exe文件中的代码要调用的函数的dll文件。方法1:一个文件,一个缓存,从文件一次性读到缓存,逆序后写回去,缺点1:无法处理大文件,缺点2:如果写回时过程异常,文件被破坏。方法4:一个文件,零缓存,使用内存映射,无需分配/释放内存和加载/写回文件数据到内存:缺点:过程异常,文件被破坏。方法3:一个文件,一个缓存,读一定大小文件内容到缓存,逆序后写回去 ,缺点:如果写回时过程异常,文件被破坏。目的:逆序一个文件的内容。

weixin_42210846的博客 1038

华为OD机试双机位C卷-分披萨 (Py/Java/C/C++/Js/Go)

华为OD机试(机考)双机位C卷 【分披萨】,提供C语言、Java、C++、Python、Go、JavaScript源码实现,并提供完整思路讲解,以及在线OJ模拟做题。华为od机试 - 分披萨。

qq_45776114的博客 1397

在 NVIDIA DGX Spark 上运行 vLLM + Open WebUI

DGX Spark 和 GX10 都是性能强大的机器,但只有将它们视为交互式工作站而非精简版服务器,才能真正发挥它们的优势。合理利用共享资源,规划并发工作负载,并谨慎控制内存限制。做到这些,vLLM 与 Open WebUI 的结合就能成为可靠​​的日常工具,而不是脆弱的实验品。DGX平台本身价格不菲,但这并非体验此类硬件的唯一途径。多家厂商提供基于相同核心架构、价格更为亲民的系统,这些系统通常配备容量较小的固态硬盘或配置更为简化,但基本性能并未改变。

iCloudEnd的博客 1751

内存映射处理大文件并实现逆序输出

上一篇介绍了一种常见的文件处理方法(可优化为:分次读取文件,但要满足根据行号能快速索引该行内容时会遇到麻烦),所以此片我将介绍另一种更高效,实用,并对本进程的内存空间地址消耗小的方法! 一. 预备知识 1). Windows编程处理文件的相关接口: A)CreateFile(); B)ReadFile(); c)WriteFile(); d)CloseHandle(); 2)

u012158162的专栏 1159

Linux内核4.14版本——drm框架分析(1)——drm简介

本文介绍了DRM架构的发展历史、驱动框架以及简单示例,旨在帮助读者了解DRM架构的形成、功能流程实现。

phmatthaus的专栏 2558

乐鑫 SoC 内存映射入门

通过阅读这份技术博客,并结合乐鑫 SoC《技术规格书》和《技术参考手册》,相信您可以更好地理解乐鑫 SoC 的内部结构。

乐鑫 Espressif 766

flash均衡磨损--实时保存设备数据

【代码】flash均衡磨损--实时保存设备数据。

Car12 480

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

字符串逆序的底层逻辑与多种实现方法详解

字符串操作是编程中的基础技能,其中逆序处理涉及字符数组的内存管理原理。从数据结构角度看,字符串本质上是连续的字符序列,逆序操作的核心在于高效交换元素位置。双指针算法通过O(n)时间复杂度实现最优性能,而栈结构和递归则展示了不同的实现思路。在实际工程中,这种技术广泛应用于密码学混淆、日志处理以及算法优化等场景。特别在处理Unicode字符串和超大文本时,需要考虑多字节编码和内存管理问题。通过比较C、Python、JavaScript等语言的实现差异,开发者可以深入理解不同编程范式下的性能优化策略。

weixin_30907935的博客 445

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

存储空间只剩10MB怎么办?边缘Agent极限优化的3个秘密方法

当边缘设备存储仅剩10MB时,如何实现Agent稳定运行?本文分享边缘设备 Agent 的存储优化三大实战方法:资源精简、增量更新与数据分层存储,适用于物联网、工业监控等场景,显著提升存储利用率。方法实用高效,值得收藏。

LearnPlex的博客 1044

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

别再只会用audioread了!手把手教你用MATLAB直接解析WAV文件头(附完整代码)

本文深入解析WAV文件的二进制结构,通过MATLAB实现底层文件头解析,涵盖RIFF格式、字节序处理和结构体解析等核心技术。相比常规的audioread函数,这种方法能更精准地提取音频参数、验证文件完整性,并支持高级音频编辑需求。附完整代码实现,帮助开发者掌握专业级音频文件处理技能。

weixin_30420305的博客 309

2.1 - 反转字符串中的单词 && 每个进程的内存里包含什么

本文包含两部分内容:1. 字符串单词反转算法:通过字符串流分割单词存入vector,逆序后重新拼接,关键步骤包括提取单词、逆序处理和空格连接。2. 进程内存结构:详细说明进程地址空间的组成部分,包括代码段、数据段(初始化/未初始化)、堆(动态内存)、栈(函数调用)、共享库映射和内核数据结构,阐述了各区域的功能特性。两部分分别提供了算法实现和系统级的内存管理知识。希望这些内容对大家有所帮助,感谢大家的三连支持!

2402_84433929的博客 935

FlatBuffers Python终极指南:高效科学计算数据序列化优化

FlatBuffers是一款由Google开发的内存高效序列化库,专为高性能需求设计,特别适合科学计算领域中处理大规模数据的场景。它通过直接在二进制缓冲区中访问数据,无需解析步骤,显著提升数据处理速度,是Python科学计算项目的理想选择。 ## 为什么选择FlatBuffers进行科学计算? 在科学计算中,数据序列化的效率直接影响整体性能。FlatBuffers凭借其独特的设计提供三大核心优

gitblog_00105的博客 896

Realm移动应用数据库使用-Android

Android系统应用集成Realm数据库

yeyu_wuhen的博客 496
上一篇: 逆序存储文件(二)——使用c标准库函数fopen,fseek,fread,fwrite
下一篇: 用三重循环求一个字符串的最大回文串(连续重复出现的最长子串)
sunchenxian017
博客等级 码龄16年 2粉丝 13原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值