之前介绍的两种逆序存储文件的方法,由于性能问题,只适用于小文件的操作,如果源文件较大,几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:对内存映射的原理不是很清楚,需要加强学习,另外文件映射的策略要根据具体的需求,这边我一次性将整个文件映射进内存,如果文件超过几十兆,就需要改变映射的策略,每次只取一部分映射,下次映射的偏移为上一次映射的大小。
48



被折叠的 条评论
为什么被折叠?



