分享
 
 
 

用Windows的文件映射机制,实现大批量数据的快速存储

王朝system·作者佚名  2006-05-12
窄屏简体版  字體: |||超大  

上次做的电子相册软件,为了解决大文件读取速度慢的问题,使用了Windows下的文件映射功能,使文件读取效率顿时得到了大幅度提升。(具体见:一个读取速度超快的FileStream!

最近在做的一款软件,又使用到了这个函数,不过这次的要求是这样的:

系统的主程序会持续的从网络端口上接收数据,这些数据需要变为实时的图像显示,但是同时图像显示部分又需要有回顾功能,也就是说能够任意将历史的数据调出来显示,为此就必须将所有历史数据保存下来,同时在需要的时候又能够快速从历史数据的指定位置将数据读出来。

针对此,有两种方案:

1)在主程序所在的机器接收数据前,使用另一台电脑,配合一个转发数据的程序,来达到数据的截取功能,由这个转发数据的程序将所有数据存储下来,并在主程序需要使用时,再用网络发送给主程序。

2)使用主程序来进行数据存储,提高主程序存储数据的性能。

不管采用何种方案,最终的瓶颈都将是大数据量的快速保存。由于整个系统内存使用和速度上的要求都很高,因此不可能将这样的数据放在程序内存里,也不可能使用普通的文件方式来记录数据。最终看来只有求助于Windows的文件映射功能了,它的优点是不会将要操作的文件读入内存,而是直接在系统层对文件进行读写,因此省去了文件的复制过程,特别是在读写大文件时,它能带来的速度提升是非常显著的。这样就可以将文件当作大内存来用了。

新的程序写完,才发现原来以前用Delphi实现的那个版本根本不能够访问大文件,只是在读取文件速度上有些优势而已,因为我过去的做法是在CreateFileMapping()之后,将整个文件的内存都MapViewOfFile()到程序内存里,这样文件一大,程序仍然无法打开文件。现在才知道,MapViewOfFile()函数是可以指定从文件的哪个部分进行Map,同时Map多少进入内存的。对于真正的大文件(几个G的)的访问,因该是一块一块的Map,然后进行读写。同时Map的起始地址和Map的内存大小都必须是64K的整数倍,不然MapViewOfFile()函数会失败。

最初的一个简单的程序版本花了1个小时不到就写完了,但是一测试却发现有个严重的问题:没有考虑数据在Map内存的边界上时的问题。当要读写的数据正好在Map的内存的边界上时,从当前Map的内存中就只能取到数据的前半部分,另一部分的数据必须Map文件的下一块地址才可能取到。因此对程序又进行了彻底的改造,让其能够在读取一个Map内存发现不够时,自动打开下一个Map内存。

总算大功告成,写了一个简单的测试程序对其进行测试,速度和正确性都都非常理想。

最后,贴上程序代码:

#ifndef enstfilecache_h

#define enstfilecache_h

#include <QtCore/QVariant>

#include <QtCore/QObject>

#include <windows.h>

#include "../enstdefine.h"

/*! \brief sampler::enstFileCache

\author tony (http://www.tonixsoft.com)

\version 0.08

\date 2006.05.10

基于文件镜像的快速大容量数据存储类。该类使用Windows下的 CreateFileMapping() 函数实现,不支持其它系统。\n

该类中的文件镜像原理可以参考:http://www.juntuan.net/hkbc/winbc/n/2006-04-19/14320.html \n

当要读取或写入的数据跨多个MapView的时候,该类会自动处理MapView的切换。

*/

class enstFileCache : public QObject

{

Q_OBJECT

public:

/*!

construct the class.

*/

enstFileCache();

/*!

destruct the class.

*/

~enstFileCache();

/*!

打开镜像文件。

@return 当打开镜像文件失败时返回false,比如磁盘空间不够。

*/

bool CreateFileCache(const QString &pFileName);

/*!

向镜像文件中追加数据。

*/

bool AppendData(T_INT8* pData, int pDataLength);

/*!

从镜像文件的指定位置读取数据。

*/

bool ReadData(T_INT64 pAddressOffset, T_INT8* pData, int pDataLength);

protected:

void DumpWindowsErrorMessage();

private:

T_INT64 mMappingViewSize;

HANDLE mFileHandle;

HANDLE mMappingHandle;

T_INT64 mWriteMappingOffset;

LPVOID mWriteBuffer;

T_INT64 mWriteBufferOffset;

T_INT64 mReadMappingOffset;

LPVOID mReadBuffer;

};

#endif //enstfilecache_h

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

#include "enstfilecache.h"

#include "../enstsvcpack.h"

//#define FILE_CACHE_SIZE 0x40000000 /* = 1GB */

#define FILE_CACHE_SIZE 0x01E00000 /* = 30MB */

enstFileCache::enstFileCache()

{

mMappingViewSize = 1024*(64*1024); //window's default block size is 64KB

mFileHandle = INVALID_HANDLE_VALUE;

mMappingHandle = NULL;

mWriteMappingOffset = 0;

mWriteBuffer = NULL;

mWriteBufferOffset = 0;

mReadMappingOffset = 0;

mReadBuffer = NULL;

}

enstFileCache::~enstFileCache()

{

if (mWriteBuffer != NULL) {

UnmapViewOfFile(mWriteBuffer);

}

if (mReadBuffer != NULL) {

UnmapViewOfFile(mReadBuffer);

}

if (mMappingHandle != NULL) {

CloseHandle(mMappingHandle);

}

if (mFileHandle != INVALID_HANDLE_VALUE) {

CloseHandle(mFileHandle);

}

}

bool enstFileCache::CreateFileCache(const QString &pFileName)

{

enstLogService *logservice = enstLogService::GetMyAddr();

mFileHandle = CreateFile(pFileName.toAscii(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

if (mFileHandle == INVALID_HANDLE_VALUE) {

DumpWindowsErrorMessage();

logservice->AppendLog(this, "Error when create file.");

return false;

}

mMappingHandle = CreateFileMapping(mFileHandle, NULL, PAGE_READWRITE, (DWORD)(FILE_CACHE_SIZE>>32), (DWORD)(FILE_CACHE_SIZE & 0xFFFFFFFF), NULL);

if (mMappingHandle == NULL) {

if (GetLastError() == 112) {

logservice->AppendLog(this, "Looks like it's not enough space on the disk.");

}

DumpWindowsErrorMessage();

logservice->AppendLog(this, "Error when create file mapping.");

return false;

}

mWriteMappingOffset = 0;

mWriteBuffer = NULL;

mWriteBufferOffset = 0;

mReadMappingOffset = 0;

mReadBuffer = NULL;

return true;

}

bool enstFileCache::AppendData(T_INT8* pData, int pDataLength)

{

int datawrote = 0;

do {

int datacanwrite = mMappingViewSize - mWriteBufferOffset;

if (mWriteBuffer && datacanwrite <= 0) {

UnmapViewOfFile(mWriteBuffer);

mWriteBuffer = NULL;

mWriteMappingOffset += mMappingViewSize;

}

if (mWriteBuffer == NULL) {

mWriteBuffer = MapViewOfFile(mMappingHandle, FILE_MAP_WRITE, (DWORD)(mWriteMappingOffset>>32), (DWORD)(mWriteMappingOffset & 0xFFFFFFFF), mMappingViewSize);

mWriteBufferOffset = 0;

datacanwrite = mMappingViewSize - mWriteBufferOffset;

if (! mWriteBuffer) {

DumpWindowsErrorMessage();

enstLogService *logservice = enstLogService::GetMyAddr();

logservice->AppendLog(this, "Error when map view of file.");

return false;

}

}

int datatowrite = pDataLength - datawrote;

int actualdatatowrite = (datacanwrite >= datatowrite)?datatowrite:datacanwrite;

memcpy((PBYTE)mWriteBuffer+mWriteBufferOffset, (PBYTE)pData+datawrote, actualdatatowrite);

mWriteBufferOffset += actualdatatowrite;

datawrote += actualdatatowrite;

} while (datawrote < pDataLength);

return true;

}

bool enstFileCache::ReadData(T_INT64 pAddressOffset, T_INT8* pData, int pDataLength)

{

int datareaded = 0;

do {

int datacanread = mReadMappingOffset + mMappingViewSize - pAddressOffset - datareaded;

if (mReadBuffer && (datacanread <= 0 || datacanread > mMappingViewSize)) {

UnmapViewOfFile(mReadBuffer);

mReadBuffer = NULL;

}

if (mReadBuffer == NULL) {

mReadMappingOffset = (pAddressOffset + datareaded) / mMappingViewSize * mMappingViewSize;

mReadBuffer = MapViewOfFile(mMappingHandle, FILE_MAP_READ, (DWORD)(mReadMappingOffset>>32), (DWORD)(mReadMappingOffset & 0xFFFFFFFF), mMappingViewSize);

datacanread = mReadMappingOffset + mMappingViewSize - pAddressOffset - datareaded;

if (! mReadBuffer) {

DumpWindowsErrorMessage();

enstLogService *logservice = enstLogService::GetMyAddr();

logservice->AppendLog(this, "Error when map view of file.");

return false;

}

}

int datatoread = pDataLength - datareaded;

int actualdatatoread = (datacanread >= datatoread)?datatoread:datacanread;

memcpy((PBYTE)pData+datareaded, (PBYTE)mReadBuffer+pAddressOffset-mReadMappingOffset+datareaded, actualdatatoread);

datareaded += actualdatatoread;

} while (datareaded < pDataLength);

return true;

}

void enstFileCache::DumpWindowsErrorMessage()

{

LPVOID lpMsgBuf;

DWORD dw = GetLastError();

FormatMessage(

FORMAT_MESSAGE_ALLOCATE_BUFFER |

FORMAT_MESSAGE_FROM_SYSTEM,

NULL,

dw,

MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),

(LPTSTR) &lpMsgBuf,

0, NULL );

enstLogService *logservice = enstLogService::GetMyAddr();

logservice->AppendLog(this, (LPTSTR)lpMsgBuf);

//puts((LPTSTR)lpMsgBuf);

LocalFree(lpMsgBuf);

}

#ifdef WIN32

#include "moc_enstfilecache.cpp"

#endif

由于系统是用QT开发的,因此类中使用了不少QT的类,同时也使用了系统中的部分工具类,不过基本工作原理相信你是能够看懂的。另外,整个系统是计划要跨平台使用的,因此今后还需要实现Linux下的类似功能,目前这个版本被绑定在Windows平台上了,不幸。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有