最近在做的一款软件,又使用到了这个函数,不过这次的要求是这样的:
系统的主程序会持续的从网络端口上接收数据,这些数据需要变为实时的图像显示,但是同时图像显示部分又需要有回顾功能,也就是说能够任意将历史的数据调出来显示,为此就必须将所有历史数据保存下来,同时在需要的时候又能够快速从历史数据的指定位置将数据读出来。
针对此,有两种方案:
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); //windows 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 its 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平台上了,不幸。
文章整理:西部数码--专业提供域名注册、虚拟主机服务
http://www.west263.com
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!


