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

2016-02-19 12:59 12 1 收藏

生活已是百般艰难,为何不努力一点。下面图老师就给大家分享用Windows的文件映射机制实现大批量数据的快速存储,希望可以让热爱学习的朋友们体会到设计的小小的乐趣。

【 tulaoshi.com - 编程语言 】

    上次做的 电子相册软件 ,为了解决大文件读取速度慢的问题,使用了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"

/*! rief sampler::enstFileCache
author tony (http://www.tonixsoft.com)
version 0.08
date 2006.05.10  

    基于文件镜像的快速大容量数据存储类。该类使用Windows下的 CreateFileMapping() 函数实现,不支持其它系统。
该类中的文件镜像原理可以参考:http://www.juntuan.net/hkbc/winbc/n/2006-04-19/14320.html
当要读取或写入的数据跨多个MapView的时候,该类会自动处理MapView的切换。
*/
class enstFileCache : public QObject
{
Q_OBJECT

public:
/*!
construct the class.
*/
enstFileCache();

/*!
destruct the class.
*/
~enstFileCache();

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

/*!
打开镜像文件。
@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

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

(本文来源于图老师网站,更多请访问http://www.tulaoshi.com/bianchengyuyan/)

#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_SIZE32), (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)(mWriteMappingOffset32), (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)(mReadMappingOffset32), (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平台上了,不幸。

来源:http://www.tulaoshi.com/n/20160219/1602712.html

延伸阅读
标签: ASP
  <% rem 文章题目 asp中利用数组实现数据库记录的批量录入方法(原创) 作者:yanek 联系email:aspboy@263.net % 包括两个文件 1。allneeddj.asp:实现表单的生成 2. allneeddjresult.asp 处理表单批量录入 3.hbedu.mdb :数据库文件 其数据库结构如下 provinceid:省份编号 数值型 dytaocount:打样套数 数值型 papertaocount:纸样套数 ...
标签: ASP
  对于网站设计者而言,时常处理大批量的文件是难免的,特别是图片和一些文本文本文件,更是经常处理。而由于网站大量文件的关系,对于同类型文件的命名,往往就直接采用带有一定规律的递增数字来作为文件名,比如,我们常见的图片文件的命名,就时常采用1001.jpg、1002.jpg这种方式,这样的好处是文件名不会重复,容易管理。这...
标签: ASP
一、创建表 tiku_koushi if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[tiku_koushi]') and OBJECTPROPERTY (id, N'IsUserTable') = 1) drop table [dbo].[tiku_koushi] GO CREATE TABLE [dbo].[tiku_koushi] ( [id] [int] IDENTITY (1, 1) NOT NULL , [title] [varchar] (250) COLLATE Chinese_PRC_CI_AS N...
本文介绍MIS SQL Server对图像数据的存储机制和存取方法。针对VB开发工具,介绍了一种通过ADO Field 对象的GetChunk 方法和AppendChunk 方法来存取MIS SQL Server中的图像数据的方法。 在一个完善的医院信息MIS中,图像数据的存取是必不可少的,比如X光片、CT像片的保存。一方面,这些图像数据在远程诊疗为准确诊断病情提供了重要的依...
众所周知,Java语言最明显的优势在于用它设计的程序可以广泛地运行于互联网上所有安装了VM解释器的计算机上。然而,如今JAVA之所以在市场上如此流行,还得益于它的另一卖点:它提供了安全可靠和使用方便的存储治理机制。这是部分编程人员将它与其前身C++语言对比后所得出的结论。本文将针对两种语言的内存(以对象为单位)使用机制,...

经验教程

700

收藏

68
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部