處理大文件之記憶體映射

来源:http://www.cnblogs.com/chanfee/archive/2017/08/08/7148159.html
-Advertisement-
Play Games

記憶體映射處理大文件 運行環境: linux 實現語言: C++ 文件大小: 大於10G 1、為什麼要用記憶體映射 a、一般讀寫大文件操作會帶來較多的磁碟IO開銷 b、數據流一次性寫入大量數據到記憶體容易達到記憶體限制 c、效率問題 2、基本概念 2.1 記憶體映射 簡單定義: 一個文件到一塊記憶體的映射。 解 ...


記憶體映射處理大文件

  • 運行環境: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 <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <sys/time.h>

#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 = "<!DOCTYPE html>"; //待查找起始字元串
string _HTML = "</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<string, string> configContent;
    std::map<string, string>::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:被映射對象內容的偏移量。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 當變數為'',false,null,undefined,0,NaN時,返回預設值 "hello world" 0 || 'ccc' "ccc" NaN || 'ccc' "ccc" 我感覺想不通一個問題 '' == false ==>true false == false ==>true null= ...
  • 由於`CoreGraphics`框架有太多的`API`,對於初次接觸或者對該框架不是十分瞭解的人,在繪圖時,對`API`的選擇會感到有些迷茫,甚至會覺得`iOS`的圖形繪製有些繁瑣。因此,本文主要介紹一下`iOS`的繪圖方法和分析一下`CoreGraphics`框架的繪圖原理。 ...
  • iOS 使用矢量圖 iOS 圖標通常用 PNG 格式的圖片。PNG 圖片放大到超過自身的大小就會模糊。可以使用 PDF 格式的矢量圖,優點是任意改變圖片大小並且保持清晰度。 簡單使用 與 PNG 格式的圖片一樣,在 Xcode 中把 PDF 格式的矢量圖拖進 \ .xcassets (預設是 Ass ...
  • 1. 給一個 UIView 加約束,希望它顯示在 UITableView 的底部,但是它不顯示,它會出現在 UITableView 的頂部。 錯誤代碼: 如圖: 原因可能是因為 UITableView 的滾動導致的,與 UIScrollView 有一致的地方。 解決: 可以顯示在 self.view ...
  • 背景 為了監控好生產環境下各個資料庫伺服器上DDL操作日誌,便於運維工程師管控好風險,我們有必要關註當前實例下的所有的DDL操作。 測試環境 Microsoft SQL Server 2012 - 11.0.2218.0 (X64) Jun 12 2012 13:05:25 Copyright (c ...
  • redis redis命令: 1、字元串: set key value//設置 get key //獲取 incr key //原子增長(一次增長1)屬於原子性 incrby key 50 //增長的值自定義 一次設置和檢索多個值 mset key1 value1 key2 value2,..., ...
  • 當收到告警信息ORA-01652: unable to extend temp segment by 128 in tablespace xxxx 時,如何Troubleshooting ORA-1652這樣的問題呢? 當然一般xxx是臨時表空間,也有可能是用戶表空間。 我們先來模擬一下這個情況,在... ...
  • 前言 在網站的用戶達到一定規模後,資料庫因為負載壓力過高而成為網站的瓶頸。幸運的是目前大部分的主流資料庫都提供主從熱備功能,通過配置兩台資料庫主從關係,可以將一臺資料庫的數據更新同步到另一臺伺服器上。網站利用資料庫的這一功能,實現資料庫讀寫分離,從而改善資料庫負載壓力。如下圖所示: 應用伺服器在寫數 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...