博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
处理大文件之内存映射
阅读量:5364 次
发布时间:2019-06-15

本文共 6653 字,大约阅读时间需要 22 分钟。

内存映射处理大文件

  • 运行环境:linux
  • 实现语言:C++
  • 文件大小:大于10G

1、为什么要用内存映射

a、一般读写大文件操作会带来较多的磁盘IO开销

b、数据流一次性写入大量数据到内存容易达到内存限制
c、效率问题

2、基本概念

2.1 内存映射

简单定义:

一个文件到一块内存的映射。

解释:

1、物理内存(Physical memory):相对于虚拟内存而言的。物理内存指通过物理内存条而获得的内存空间。

2、虚拟内存(Virtual memory):虚拟内存则是指将硬盘的一块区域划分来作为内存。其主要作用是在计算机运行时为操作系统和各种程序提供临时储存。
3、内存映射(Memory map):与虚拟内存类似。利用进程中与磁盘上的文件大小相同的逻辑内存进行映射,并进行寻址访问,其过程就如同对加载了文件的内存空间进行访问。

3、方案

通过nmap(一种系统调用方法)将磁盘文件映射进内存。

3.1 实例:利用内存映射对爬虫采集的html页面数据进行过滤处理

#include 
#include
#include
#include
#include
#include "header/commonForAll.h"#include "header/splitHtmlFromNutch.h"#include "header/memoryMapping.h"#include "header/cParseIniFile.h"using namespace std;int htmlSplitNum = 0;int viewMapSize = 0;char* htmloutput;char* outputFileCommon; //输出文件公共文件名部分string::size_type STR_FIND_RETURN; //查找字符串返回值string DOCTYPE = ""; //待查找起始字符串string _HTML = ""; //待查找结束字符串bool IS_WRITING = false;int main(int argc, char **argv) { /********加入时间测试********/ struct timeval start; struct timeval end; unsigned long timer; gettimeofday(&start,NULL); /***********************读取配置文件**********************/ /**************************开始************************/ CParseIniFile parseIniFile; const string configPath = "/home/chaffee/work/projects/semantic_library/cpp/SplitHtmlFromNutch/splitHtmlFromNutch.ini"; //判断配置文件是否存在 if(!CommonForAll::isExistFile((char*)configPath.c_str())){ cout << "该文件splitHtmlFromNutch.ini不存在" << endl; return 0; } std::map
configContent; std::map
::iterator iter; const char* configSection = "main_config"; parseIniFile.ReadConfig(configPath, configContent, configSection); /**************************结束************************/ htmloutput = (char*)configContent["htmloutput"].c_str(); outputFileCommon = (char*)configContent["outputfilecommon"].c_str(); htmlSplitNum = atoi(configContent["htmlsplitnum"].c_str()); viewMapSize = atoi(configContent["viewmapsize"].c_str()); SplitHtmlFromNutch shfromNuntch(configContent["htmlinput"].c_str(), htmlSplitNum); if (!shfromNuntch.ISEXIST_INPUTFILE()) { cout << "输入文件不存在" << endl; return 0; } //判断输出目录是否存在 if(!CommonForAll::isExistDir(htmloutput)){ cout << "输出目录不存在" << endl; return 0; } MemoryMapping memoryMap; //设置内存映射分页大小 memoryMap.setViewMapSize(viewMapSize); //获取有效的文件描述 int fd = CommonForAll::getFd((char*)shfromNuntch.HTMLPATH); if(fd < 0){ cout << "输入文件错误" << endl; return 0; } //获取文件大小 long fileLen = CommonForAll::getFileSize((char*)shfromNuntch.HTMLPATH); if(fileLen <= 0){ cout << "输入文件大小为0" << endl; return 0; } long count = 0; //分页计数 long int offset = 0; //分页偏移量 ofstream ofstrFile; int outFileCount = 0; int htmlCount = 0; /*********输出文件路径拼接**********/ char htmlOutputDir[512]; //兼容输出路径是否带'/' if(!(htmloutput[strlen(htmloutput) - 1] == '/')){ sprintf(htmloutput, "%s%c", htmloutput, '/'); } snprintf(htmlOutputDir, sizeof(htmlOutputDir), "%s%s%d%s", htmloutput, outputFileCommon, outFileCount, ".html"); ofstrFile.open(htmlOutputDir, ios::out); //====================开始分页映射操作======================= while((fileLen - count * memoryMap.VIEWMAPSIZE) > 0){ memoryMap.doPaging(memoryMap.VIEWMAPSIZE, fd, offset); const char* mmapBuf = memoryMap.memMap; const char* mmapStart = memoryMap.memMap; int len = 0; while(mmapStart != NULL){ mmapStart = CommonForAll::_get_line(mmapBuf,&len); string strLine(mmapBuf,len); if(!strLine.empty()){ //按照一百个html页面进行分割 //------------------start------------------ if (100 == htmlCount) { outFileCount++; //用snprintf代替sprintf,标明大小sizeof(htmlOutputDir),防止核心内存操作错误 snprintf(htmlOutputDir, sizeof(htmlOutputDir), "%s%s%d%s", htmloutput, outputFileCommon, outFileCount, ".html"); ofstrFile.flush(); ofstrFile.clear(); ofstrFile.close(); ofstrFile.open(htmlOutputDir); htmlCount = 0; } if (IS_WRITING) { STR_FIND_RETURN = strLine.find(_HTML); if (STR_FIND_RETURN != string::npos) { if (ofstrFile.is_open()) { ofstrFile << strLine << endl; } IS_WRITING = false; htmlCount++; } else { if (ofstrFile.is_open()) { ofstrFile << strLine << endl; } } } else { STR_FIND_RETURN = strLine.find(DOCTYPE); if (STR_FIND_RETURN != string::npos) { IS_WRITING = true; if (ofstrFile.is_open()) { ofstrFile << strLine << endl; } } } } //------------------end------------------ mmapBuf = mmapStart; } offset += memoryMap.VIEWMAPSIZE; count++; munmap(memoryMap.memMap, memoryMap.VIEWMAPSIZE); msync(memoryMap.memMap, memoryMap.VIEWMAPSIZE, MS_SYNC); memoryMap.memMap = NULL; } if(ofstrFile){ ofstrFile.flush(); ofstrFile.clear(); ofstrFile.close(); } close(fd); /********打印测试时间**********/ gettimeofday(&end,NULL); timer = 1000000 * (end.tv_sec-start.tv_sec)+ end.tv_usec-start.tv_usec; printf("程序用时 = %ld us\n",timer); return 0;}

核心映射类:

#include "header/memoryMapping.h"using namespace std;MemoryMapping::MemoryMapping()    : memMap(NULL)    , VIEWMAPSIZE(0){}MemoryMapping::~MemoryMapping() {    if (this->memMap)    {        munmap(this->memMap, this->VIEWMAPSIZE);        msync(this->memMap, this->VIEWMAPSIZE, MS_SYNC);    }    //cout << "内存映射析构方法" << endl;}void MemoryMapping::setViewMapSize(size_t length) {    this->VIEWMAPSIZE = length;}bool MemoryMapping::doPaging(size_t length, int fd, off_t offset) {    this->memMap = (char*)mmap(NULL, length, PROT_READ, MAP_SHARED, fd, offset);    if(this->memMap != NULL)    {        return true;    }else{        return false;    }}

映射函数及参数解释(引自百度百科):

void* mmap(void* start,size_t length,int prot,int flags,int fd,off_t offset);

start:映射区的开始地址,设置为0或者null时表示由系统决定映射区的起始地址。

length:映射区的长度。长度单位是以字节(KB)为单位,不足一个内存页按一个内存页处理。
prot:期望的内存保护标志,不能与文件的打开模式冲突。通过下列的单个或者利用or运算合理地组合(类似于linux的文件权限系统)。

  1. PROT_EXEC //页内容可以被执行。
  2. PROT_READ //页内容可以被读取。
  3. PROT_WRITE //页可以被写入。
  4. PROT_NONE  //页不可访问。

flags:指定映射对象的类型,映射选项和映射页是否可以共享。

  1. MAP_FIXED //使用指定的映射起始地址,如果由start和length参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
  2. MAP_SHARED //与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
  3. MAP_PRIVATE //建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和以上标志是互斥的,只能使用其中一个。
  4. MAP_DENYWRITE //这个标志被忽略。
  5. MAP_EXECUTABLE //同上。
  6. MAP_NORESERVE //不要为这个映射保留交换空间。当交换空间被保留,对映射区修改的可能会得到保证。当交换空间不被保留,同时内存不足,对映射区的修改会引起段违例信号。
  7. MAP_LOCKED //锁定映射区的页面,从而防止页面被交换出内存。
  8. MAP_GROWSDOWN //用于堆栈,告诉内核VM系统,映射区可以向下扩展。
  9. MAP_ANONYMOUS //匿名映射,映射区不与任何文件关联。
  10. MAP_ANON //MAP_ANONYMOUS的别称,不再被使用。
  11. MAP_FILE //兼容标志,被忽略。
  12. MAP_32BIT //将映射区放在进程地址空间的低2GB,MAP_FIXED指定时会被忽略。当前这个标志只在x86-64平台上得到支持。
  13. MAP_POPULATE //为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
  14. MAP_NONBLOCK //仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。

fd:有效的文件描述词。一般是由open()函数返回,其值也可以设置为-1,此时需要指定flags参数中的MAP_ANON,表明进行的是匿名映射。

off_toffset:被映射对象内容的偏移量。

转载于:https://www.cnblogs.com/chanfee/p/7148159.html

你可能感兴趣的文章
网页中插入透明Flash的方法和技巧
查看>>
动态内存申请函数选择(realloc、malloc 、alloca、 calloc)
查看>>
获取元素属性get_attribute
查看>>
视觉设计师的进化
查看>>
Python/jquery
查看>>
【BZOJ】【2132】圈地计划
查看>>
Lua 语言基本语法
查看>>
ARM 的Thumb状态测试
查看>>
windows下读取utf-8文件
查看>>
apache 启动不了的排查方法
查看>>
Java有没有goto?
查看>>
(转)makefile 的用法
查看>>
求不相邻金币相加和的最大值--动态规划1
查看>>
[转][osg]探索未知种族之osg类生物【目录】
查看>>
四十九. Zabbix报警机制 、 Zabbix进阶操作 、 监控案例
查看>>
元类中__new__ 与 __init__的区别--day27
查看>>
占小狼的简书博客
查看>>
struts2__action执行顺序
查看>>
php异常处理
查看>>
[xampp] /usr/bin/env: php: No such file or directory
查看>>