电信主站 网通分站
购买流程 付款方式 常见问题 在线提问 续租服务 购物车
用户名: 密 码: 忘记密码?
首 页
域名注册
虚拟主机
双线主机
服务器租用
VPS主机
企业邮局
代理专区
客服中心
虚拟主机行业资讯 虚拟主机评测对比 互联网最新动态 技术学院 站长资讯 在线教程 网站运营
搜索优化 服务器 网络编程 图形图象 站长之家 网页制作 操作系统
冲浪宝典 软件教学 视频通信 办公软件 邮件系统 网络安全 认证考试
您当前位置:西部数码->资讯中心-> 在线教程-> ASP
用Windows的文件映射机制,实现大批量数据的快速存储-ASP教程,系统相关
作者:网友供稿 点击:184
  西部数码-全国虚拟主机10强!20余项虚拟主机管理功能,全国领先!第6代双线路虚拟主机,南北访问畅通无阻!虚拟主机可在线rar解压,自动数据恢复设置虚拟目录等.虚拟主机免费赠送访问统计,企业邮局.Cn域名注册10元/年,自助建站480元起,免费试用7天,满意再付款!P4主机租用799元/月.月付免压金!
文章页数:[1] 
上次做的电子相册软件,为了解决大文件读取速度慢的问题,使用了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); //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
以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!
相关主题
文章页数:[1] 
Google
热门文章
·如何使XP的目录属性出现"安全"选项-ASP教程,系统相关
·创建有个性的对话框之MFC篇(二)-ASP教程,系统相关
·用InstallShield打包ASP程序-ASP教程,ASP应用
·windows server 2003 中 SQL Server 2000 分布式事务 错误解决方法-ASP教程,系统相关
·创建有个性的对话框之MFC篇(一)-ASP教程,系统相关
·DevExpress打印相关代码-ASP教程,打印相关
·File文件控件,选中文件(图片,flash,视频)即立即预览显示-ASP教程,组件开发
·用Windows的文件映射机制,实现大批量数据的快速存储-ASP教程,系统相关
·ADO如何取得数据库中表的字段信息之一
·使用DEVEXPRESS部件打印时标题的处理-ASP教程,打印相关

最新文章
· SQL注入天书 - ASP注入漏洞全接触
·用.net 处理xmlHttp发送异步请求
·asp.net创建文件夹的IO类的问题
·如何实现ASP.NET网站个性化
·关于ASP.NET调用JavaScript的实现
·ASP利用Google实现在线翻译功能
·Asp无组件生成缩略图
·由HTTP 500 Internal server error想到的...
·实例讲解asp抓取网上房产信息
·改mdb为asp所带来的灾难


 
 


版权申明:本站文章均来自网络,如有侵权,请联系我们,我们收到后立即删除,谢谢!

特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有。
  打印  刷新  关闭
返回首页 |关于我们 | 联系我们 | 付款方式 | 创业联盟 | 虚拟主机 | 资讯中心 | 友情链接 | 网站地图

版权所有 西部数码(www.west263.com)
CopyRight (c) 2002~2006 west263.com all right reserved.
公司地址:四川成都市万和路90号天象大厦4楼 邮编:610031
电话总机:028-86262244 86263048 86263408 86263960 86264018 86267838
售前咨询:总机转201 202 203 204 206 208
售后服务:总机转211 212 213 214
财务咨询:总机转224 223 传真:028-86264041 财务QQ:点击发送消息给对方635483282
售前咨询QQ:点击发送消息给对方2182518 点击发送消息给对方241975952 点击发送消息给对方275026793 点击发送消息给对方408235859
售后服务QQ:点击发送消息给对方17708515 点击发送消息给对方307742704 点击发送消息给对方287976517 点击发送消息给对方363783715
《中华人民共和国增值电信业务经营许可证》编号:川B2-20030065号