• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            woaidongmao

            文章均收錄自他人博客,但不喜標(biāo)題前加-[轉(zhuǎn)貼],因其丑陋,見諒!~
            隨筆 - 1469, 文章 - 0, 評(píng)論 - 661, 引用 - 0
            數(shù)據(jù)加載中……

            內(nèi)存映射文件使用詳細(xì)(圖不錯(cuò))

            摘要: 本文通過內(nèi)存映射文件的使用來對(duì)大尺寸文件進(jìn)行訪問操作,同時(shí)也對(duì)內(nèi)存映射文件的相關(guān)概念和一般編程過程作了較為詳細(xì)的介紹。


              關(guān)鍵詞: 內(nèi)存映射文件;大文件處理;分配粒度

             引言
              文件操作是應(yīng)用
            程序最為基本的功能之一,Win32 APIMFC均提供有支持文件處理的函數(shù)和類,常用的有Win32 APICreateFile()WriteFile()ReadFile()MFC提供的CFile類等。一般來說,以上這些函數(shù)可以滿足大多數(shù)場合的要求,但是對(duì)于某些特殊應(yīng)用領(lǐng)域所需要的動(dòng)輒幾十GB、幾百GB、乃至幾TB的海量存儲(chǔ),再以通常的文件處理方法進(jìn)行處理顯然是行不通的。目前,對(duì)于上述這種大文件的操作一般是以內(nèi)存映射文件的方式來加以處理的,本文下面將針對(duì)這種Windows核心編程技術(shù)展開討論。

              內(nèi)存映射文件概述

              內(nèi)存文件映射也是Windows的一種內(nèi)存管理方法,提供了一個(gè)統(tǒng)一的內(nèi)存管理特征,使應(yīng)用程序可以通過內(nèi)存指針對(duì)磁盤上的文件進(jìn)行訪問,其過程就如同對(duì)加載了文件的內(nèi)存的訪問。通過文件映射這種使磁盤文件的全部或部分內(nèi)容與進(jìn)程虛擬地址空間的某個(gè)區(qū)域建立映射關(guān)聯(lián)的能力,可以直接對(duì)被映射的文件進(jìn)行訪問,而不必執(zhí)行文件I/O操作也無需對(duì)文件內(nèi)容進(jìn)行緩沖處理。內(nèi)存文件映射的這種特性是非常適合于用來管理大尺寸文件的。

              在使用內(nèi)存映射文件進(jìn)行I/O處理時(shí),系統(tǒng)對(duì)數(shù)據(jù)的傳輸按頁面來進(jìn)行。至于內(nèi)部的所有內(nèi)存頁面則是由虛擬內(nèi)存管理器來負(fù)責(zé)管理,由其來決定內(nèi)存頁面何時(shí)被分頁到磁盤,哪些頁面應(yīng)該被釋放以便為其它進(jìn)程提供空閑空間,以及每個(gè)進(jìn)程可以擁有超出實(shí)際分配物理內(nèi)存之外的多少個(gè)頁面空間等等。由于虛擬內(nèi)存管理器是以一種統(tǒng)一的方式來處理所有磁盤I/O的(以頁面為單位對(duì)內(nèi)存數(shù)據(jù)進(jìn)行讀寫),因此這種優(yōu)化使其有能力以足夠快的速度來處理內(nèi)存操作。

              使用內(nèi)存映射文件時(shí)所進(jìn)行的任何實(shí)際I/O交互都是在內(nèi)存中進(jìn)行并以標(biāo)準(zhǔn)的內(nèi)存地址形式來訪問。磁盤的周期性分頁也是由操作系統(tǒng)在后臺(tái)隱蔽實(shí)現(xiàn)的,對(duì)應(yīng)用程序而言是完全透明的。內(nèi)存映射文件的這種特性在進(jìn)行大文件的磁盤事務(wù)操作時(shí)將獲得很高的效益。

              需要說明的是,在系統(tǒng)的正常的分頁操作過程中,內(nèi)存映射文件并非一成不變的,它將被定期更新。如果系統(tǒng)要使用的頁面目前正被某個(gè)內(nèi)存映射文件所占用,系統(tǒng)將釋放此頁面,如果頁面數(shù)據(jù)尚未保存,系統(tǒng)將在釋放頁面之前自動(dòng)完成頁面數(shù)據(jù)到磁盤的寫入。

              對(duì)于使用頁虛擬存儲(chǔ)管理的Windows操作系統(tǒng),內(nèi)存映射文件是其內(nèi)部已有的內(nèi)存管理組件的一個(gè)擴(kuò)充。由可執(zhí)行代碼頁面和數(shù)據(jù)頁面組成的應(yīng)用程序可根據(jù)需要由操作系統(tǒng)來將這些頁面換進(jìn)或換出內(nèi)存。如果內(nèi)存中的某個(gè)頁面不再需要,操作系統(tǒng)將撤消此頁面原擁用者對(duì)它的控制權(quán),并釋放該頁面以供其它進(jìn)程使用。只有在該頁面再次成為需求頁面時(shí),才會(huì)從磁盤上的可執(zhí)行文件重新讀入內(nèi)存。同樣地,當(dāng)一個(gè)進(jìn)程初始化啟動(dòng)時(shí),內(nèi)存的頁面將用來存儲(chǔ)該應(yīng)用程序的靜態(tài)、動(dòng)態(tài)數(shù)據(jù),一旦對(duì)它們的操作被提交,這些頁面也將被備份至系統(tǒng)的頁面文件,這與可執(zhí)行文件被用來備份執(zhí)行代碼頁面的過程是很類似的。圖1展示了代碼頁面和數(shù)據(jù)頁面在磁盤存儲(chǔ)器上的備份過程:

            clip_image001
            1 進(jìn)程的代碼頁、數(shù)據(jù)頁在磁盤存儲(chǔ)器上的備份

              顯然,如果可以采取同一種方式來處理代碼和數(shù)據(jù)頁面,無疑將會(huì)提高程序的執(zhí)行效率,而內(nèi)存映射文件的使用恰恰可以滿足此需求。

             

              對(duì)大文件的管理

              內(nèi)存映射文件對(duì)象在關(guān)閉對(duì)象之前并沒有必要撤銷內(nèi)存映射文件的所有視圖。在對(duì)象被釋放之前,所有的臟頁面將自動(dòng)寫入磁盤。通過CloseHandle()關(guān)閉內(nèi)存映射文件對(duì)象,只是釋放該對(duì)象,如果內(nèi)存映射文件代表的是磁盤文件,那么還需要調(diào)用標(biāo)準(zhǔn)文件I/O函數(shù)來將其關(guān)閉。在處理大文件處理時(shí),內(nèi)存映射文件將表示出卓越的優(yōu)勢,只需要消耗極少的物理資源,對(duì)系統(tǒng)的影響微乎其微。下面先給出內(nèi)存映射文件的一般編程流程框圖:

            clip_image002
            2 使用內(nèi)存映射文件的一般流程

              而在某些特殊行業(yè),經(jīng)常要面對(duì)十幾GB乃至幾十GB容量的巨型文件,而一個(gè)32位進(jìn)程所擁有的虛擬地址空間只有232 = 4GB,顯然不能一次將文件映像全部映射進(jìn)來。對(duì)于這種情況只能依次將大文件的各個(gè)部分映射到進(jìn)程中的一個(gè)較小的地址空間。這需要對(duì)上面的一般流程進(jìn)行適當(dāng)?shù)母模?span lang="EN-US">

              1)映射文件開頭的映像。

              2)對(duì)該映像進(jìn)行訪問。

              3)取消此映像

              4)映射一個(gè)從文件中的一個(gè)更深的位移開始的新映像。

              5)重復(fù)步驟2,直到訪問完全部的文件數(shù)據(jù)。

              下面給出一段根據(jù)此描述而寫出的對(duì)大于4GB的文件的處理代碼:

            // 選擇文件
            CFileDialog fileDlg(TRUE, "*.txt", "*.txt", NULL, "
            文本文件 (*.txt)|*.txt||", this);
            fileDlg.m_ofn.Flags |= OFN_FILEMUSTEXIST;
            fileDlg.m_ofn.lpstrTitle = "
            通過內(nèi)存映射文件讀取數(shù)據(jù)";
            if (fileDlg.DoModal() == IDOK)
            {
             // 創(chuàng)建文件對(duì)象
             HANDLE hFile = CreateFile(fileDlg.GetPathName(), GENERIC_READ | GENERIC_WRITE,
               0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
             if (hFile == INVALID_HANDLE_VALUE)
             {
              TRACE("創(chuàng)建文件對(duì)象失敗,錯(cuò)誤代碼:%d\r\n", GetLastError());
              return;
             }
             // 創(chuàng)建文件映射對(duì)象
             HANDLE hFileMap = CreateFileMapping(hFile, NULL, PAGE_READWRITE, 0, 0, NULL);
             if (hFileMap == NULL)
             {
              TRACE("創(chuàng)建文件映射對(duì)象失敗,錯(cuò)誤代碼:%d\r\n", GetLastError());
              return;
             }
             // 得到系統(tǒng)分配粒度
             SYSTEM_INFO SysInfo;
             GetSystemInfo(&SysInfo);
             DWORD dwGran = SysInfo.dwAllocationGranularity;
             // 得到文件尺寸
             DWORD dwFileSizeHigh;
             __int64 qwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
             qwFileSize |= (((__int64)dwFileSizeHigh) << 32);
             // 關(guān)閉文件對(duì)象
             CloseHandle(hFile);
             // 偏移地址
             __int64 qwFileOffset = 0;
             // 塊大小
             DWORD dwBlockBytes = 1000 * dwGran;
             if (qwFileSize < 1000 * dwGran)
              dwBlockBytes = (DWORD)qwFileSize;
              while (qwFileOffset > 0)
              {
               // 映射視圖
               LPBYTE lpbMapAddress = (LPBYTE)MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,
                  (DWORD)(qwFileOffset >> 32), (DWORD)(qwFileOffset & 0xFFFFFFFF),
                  dwBlockBytes);
               if (lpbMapAddress == NULL)
               {
                TRACE("映射文件映射失敗,錯(cuò)誤代碼:%d\r\n", GetLastError());
                return;
               }
               // 對(duì)映射的視圖進(jìn)行訪問
               for(DWORD i = 0; i < dwBlockBytes; i++)
                BYTE temp = *(lpbMapAddress + i);
                // 撤消文件映像
                UnmapViewOfFile(lpbMapAddress);
                // 修正參數(shù)
                qwFileOffset += dwBlockBytes;
                qwFileSize -= dwBlockBytes;
              }
              // 關(guān)閉文件映射對(duì)象句柄
              CloseHandle(hFileMap);
              AfxMessageBox("成功完成對(duì)文件的訪問");
            }


              在本例中,首先通過GetFileSize()得到被處理文件長度(64位)的高32位和低32位值。然后在映射過程中設(shè)定每次映射的塊大小為1000倍的分配粒度,如果文件長度小于1000倍的分配粒度時(shí)則將塊大小設(shè)置為文件的實(shí)際長度。在處理過程中由映射、訪問、撤消映射構(gòu)成了一個(gè)循環(huán)處理。其中,每處理完一個(gè)文件塊后都通過關(guān)閉文件映射對(duì)象來對(duì)每個(gè)文件塊進(jìn)行整理。CreateFileMapping()、MapViewOfFile()等函數(shù)是專門用來進(jìn)行內(nèi)存文件映射處理用的。

             

             下面分別對(duì)這些關(guān)鍵函數(shù)進(jìn)行說明:

              1CreateFile():CreateFile()函數(shù)是一個(gè)用途非常廣泛的函數(shù), 在這里的用法并沒有什么特殊的地方,但有幾點(diǎn)需要注意:一是訪問模式參數(shù)dwDesiredAccess。該參數(shù)設(shè)置了對(duì)文件內(nèi)核對(duì)象的訪問類型,其允許設(shè)置的權(quán)限可以為讀權(quán)限GENERIC_READ、寫權(quán)限GENERIC_WRITE、讀寫權(quán)限GENERIC_READ | GENERIC_WRITE和設(shè)備查詢權(quán)限0。在使用映射文件時(shí),只能打開那些具有可讀訪問權(quán)限的文件,即只能應(yīng)用GENERIC_READGENERIC_READ | GENERIC_WRITE這兩種組合;另一點(diǎn)需要注意的是共享模式參數(shù)dwShareMode。該參數(shù)定義了對(duì)文件內(nèi)核對(duì)象的共享方式,其可能的設(shè)置為FILE_SHARE_READFILE_SHARE_WRITE0,并可對(duì)其組合使用。其中,設(shè)置為0時(shí)不允許共享對(duì)象;FILE_SHARE_READFILE_SHARE_WRITE分別為在要求只讀、只寫訪問的情況下才允許對(duì)象的共享。

              由于通過內(nèi)存映射文件可以在多個(gè)進(jìn)程間共享數(shù)據(jù),因此在進(jìn)行這種應(yīng)用時(shí)應(yīng)當(dāng)考慮dwShareMode參數(shù)設(shè)置對(duì)運(yùn)行結(jié)果的影響。

              2CreateFileMapping():該函數(shù)的作用是創(chuàng)建一個(gè)文件映射內(nèi)核對(duì)象,以告知系統(tǒng)文件映射對(duì)象需要多大的物理存儲(chǔ)器。創(chuàng)建內(nèi)存映射文件對(duì)象對(duì)系統(tǒng)資源幾乎沒有什么影響,也不會(huì)影響進(jìn)程的虛擬地址空間。除了需要用來表示該對(duì)象的內(nèi)部資源之外通常并不用為其分配虛擬內(nèi)存,但是如果內(nèi)存映射文件對(duì)象是作共享內(nèi)存之用的話,就要在創(chuàng)建對(duì)象時(shí)由系統(tǒng)為內(nèi)存映射文件的使用在系統(tǒng)頁文件中保留足夠的空間。

              函數(shù)第一個(gè)參數(shù)hFile為標(biāo)識(shí)要映射到進(jìn)程的地址空間的文件的句柄。雖然由于內(nèi)存映射文件的物理存儲(chǔ)器是來自于磁盤上的文件,而非系統(tǒng)的頁文件,使創(chuàng)建內(nèi)存映射文件就像保留一個(gè)地址空間區(qū)域并將物理存儲(chǔ)器提交給該區(qū)域一樣。第二個(gè)參數(shù)為指向文件映射內(nèi)核對(duì)象的SECURITY_ATTRIBUTES結(jié)構(gòu)的指針,由此來決定子進(jìn)程能否繼承得到返回的句柄。通常為其傳遞NULL值,以默認(rèn)的安全屬性來禁止返回句柄的被繼承。

              接下來的參數(shù)用于文件被映射后設(shè)定文件映像的保護(hù)屬性。其可能的取值為PAGE_READONLYPAGE_READWRITEPAGE_WRITECOPY。雖然在創(chuàng)建文件映射對(duì)象時(shí),系統(tǒng)并不為其保留地址空間區(qū)域,也不將文件的存儲(chǔ)器映射到該區(qū)域。但是,在系統(tǒng)將存儲(chǔ)器映射到進(jìn)程的地址空間中去時(shí),系統(tǒng)必須確切知道應(yīng)賦予物理存儲(chǔ)器頁面的保護(hù)屬性。在設(shè)置保護(hù)屬性時(shí),必須與用CreateFile()函數(shù)打開文件時(shí)所指定的訪問標(biāo)識(shí)相匹配,否則將導(dǎo)致CreateFileMapping()的執(zhí)行失敗。因此這里設(shè)置PAGE_READWRITE屬性。除了上述三個(gè)頁面保護(hù)屬性外,還有4個(gè)區(qū)(Section)保護(hù)屬性也可以一起組合使用:

            區(qū)保護(hù)屬性

            說明

            SEC_COMMIT

            為區(qū)中的所有頁面在內(nèi)存中或磁盤頁面文件中分配物理存儲(chǔ)器

            SEC_IMAGE

            告知系統(tǒng),映射的文件是一個(gè)可移植的EXE文件映像

            SEC_NOCACHE

            告知系統(tǒng),未將文件的任何內(nèi)存映射文件放入高速緩存,多供硬件設(shè)備驅(qū)動(dòng)程序開發(fā)人員使用

            SEC_RESERVE

            對(duì)一個(gè)區(qū)的所有頁面進(jìn)行保留而不分配物理存儲(chǔ)器


              后面的兩個(gè)參數(shù)指定了要?jiǎng)?chuàng)建的文件映射對(duì)象的最大字節(jié)數(shù)的高32位值和低32位值,實(shí)際也就設(shè)定了文件的最大字節(jié)數(shù)(最大可以處理16EB的文件)。這兩個(gè)參數(shù)可以滿足確保文件映射對(duì)象能夠得到足夠的物理存儲(chǔ)器這一基本條件。在參數(shù)設(shè)置的大小小于文件實(shí)際大小時(shí),系統(tǒng)將從文件映射指定的字節(jié)數(shù)。這里將其設(shè)置為0,將使所創(chuàng)建的文件映射對(duì)象將為文件的當(dāng)前大小,以上兩種情況均無法改變文件的大小。如果設(shè)置的參數(shù)大于文件的實(shí)際大小,系統(tǒng)將會(huì)在CreateFileMapping()函數(shù)返回前擴(kuò)展該文件。需要指出的是,文件映射對(duì)象的大小是靜態(tài)的,一旦創(chuàng)建完畢后將無法更改。如果設(shè)置的文件映射對(duì)象尺寸偏小將導(dǎo)致無法對(duì)文件進(jìn)行全面的訪問。

              在本節(jié)開始也曾提到過,創(chuàng)建文件映射對(duì)象是不需要花費(fèi)什么系統(tǒng)資源的,因此遵循"寧多勿缺"的原則,一般應(yīng)將文件映射對(duì)象的大小設(shè)置為文件大小的相同值。函數(shù)最后的參數(shù)將可以為映射對(duì)象命名。如果想打開一個(gè)已存在的文件映射對(duì)象,該對(duì)象必須要命名。對(duì)該名字字符串的要求僅限于未被其它對(duì)象使用過的名字即可。

              CreateFileMapping()在成功執(zhí)行后將返回一個(gè)指向文件映射對(duì)象的句柄。如果對(duì)一個(gè)已經(jīng)存在的文件映射對(duì)象調(diào)用了CreateFileMapping()函數(shù),進(jìn)程將得到一個(gè)指向現(xiàn)有映射對(duì)象的句柄。通過調(diào)用GetLastError()可以得到返回值ERROR_ALREADY_EXIST,由此可以判斷當(dāng)前得到的內(nèi)存映射對(duì)象句柄是新創(chuàng)建的還是打開已經(jīng)存在的。如果系統(tǒng)無法創(chuàng)建文件映射對(duì)象,將導(dǎo)致CreateFileMapping()的執(zhí)行失敗,返回N U L L句柄值。

             

              3MapViewOfFile():當(dāng)創(chuàng)建了一個(gè)內(nèi)存映射文件對(duì)象并得到其有效句柄后,該句柄即可用來在進(jìn)程的虛擬地址空間中映射文件的一個(gè)映像。在內(nèi)存映射文件對(duì)象已經(jīng)存在的情況下,映像可被任意映射或取消映射。在文件映像被映射時(shí),仍然必須由系統(tǒng)來為文件的數(shù)據(jù)保留一個(gè)地址空間區(qū)域,并將文件的數(shù)據(jù)作為映射到該區(qū)域的物理存儲(chǔ)器進(jìn)行提交。在進(jìn)程的地址空間中,一個(gè)足夠大的連續(xù)地址空間(通常足以覆蓋整個(gè)文件映像)將被指定給此文件映像。盡管如此,內(nèi)存的物理頁面還是根據(jù)在實(shí)際使用中的需求而進(jìn)行分配的。真正分配一個(gè)對(duì)應(yīng)于內(nèi)存映射文件映像頁面的物理內(nèi)存頁面是在發(fā)生該頁的缺頁中斷時(shí)進(jìn)行的,這將在第一次讀寫內(nèi)存頁面中的任一地址時(shí)自動(dòng)完成。MapViewOfFile()即負(fù)責(zé)映射內(nèi)存映射文件的一個(gè)映像,

              函數(shù)的第一個(gè)參數(shù)為CreateFileMapping()所返回的內(nèi)存映射文件對(duì)象句柄,第二個(gè)參數(shù)指定了對(duì)文件映像的訪問類型,可能取值有FILE_MAP_WRITEFILE_MAP_READFILE_MAP_ALL_ACCESSFILE_MAP_COPY等幾種,具體的設(shè)置要根據(jù)文件映射對(duì)象允許的保護(hù)模式而定。根據(jù)前面代碼的設(shè)置,這里應(yīng)該使用FILE_MAP_ALL_ACCESS參數(shù)。這種機(jī)制為對(duì)象的創(chuàng)建者提供了對(duì)映射此對(duì)象的方式進(jìn)行控制的能力。接下來的2個(gè)參數(shù)分別指定了內(nèi)存映射文件的64位偏移地址的低32位和高32位地址,該地址是從內(nèi)存映射文件頭位置到映像開始位置的距離。最后的參數(shù)指定了視圖的大小,如果設(shè)置為0,前面的偏移地址將被忽略,系統(tǒng)將會(huì)把整個(gè)文件映射為一個(gè)映像。MapViewOfFile()如果成功執(zhí)行,將返回一個(gè)指向文件映像在進(jìn)程的地址空間中的起始地址的指針。如果失敗,則返回NULL。在進(jìn)程中,可以為同一個(gè)文件映射對(duì)象創(chuàng)建多個(gè)文件映像,這些映像可以在系統(tǒng)中共存和重疊,也可以與對(duì)應(yīng)的文件映射對(duì)象大小不相一致,但不能大于文件映射對(duì)象的大小。

              4UnmapViewOfFile():當(dāng)不再需要保留映射到進(jìn)程地址空間區(qū)域中的文件映像數(shù)據(jù)時(shí),可通過調(diào)用UnmapViewOfFile()函數(shù)將其釋放。該函數(shù)結(jié)構(gòu)非常簡單,只需要提供映像在進(jìn)程中的起始地址(區(qū)域的基地址)作為參數(shù)即可。該函數(shù)的輸入?yún)?shù)為調(diào)用MapViewOfFile()時(shí)所返回的指向文件映像在進(jìn)程的地址空間中的起始地址的指針。在調(diào)用MapViewOfFile()后,必須確保在進(jìn)程退出之前能夠執(zhí)行UnmapViewOfFile()函數(shù),否則在進(jìn)程終止之后先前保留的區(qū)域?qū)⒌貌坏结尫牛词乖俅螁?dòng)進(jìn)程重復(fù)調(diào)用MapViewOfFile()系統(tǒng)也總是在進(jìn)程的地址空間中保留一個(gè)新的區(qū)域,而此前保留的所有區(qū)域?qū)⒌貌坏结尫拧?span lang="EN-US">

              一種比較特殊的情況是,對(duì)同一個(gè)內(nèi)存映射文件映射了兩個(gè)相同的映像的撤消。前面曾經(jīng)提到過,對(duì)于同一個(gè)內(nèi)存映射文件可以有多個(gè)映像,這些映像也可以重疊,因此這種情況的存在是合法的。對(duì)于這種情況,雖然從表面看上去在單進(jìn)程的地址空間內(nèi)是不可能存在兩個(gè)基地址完全相同的映像的,這將導(dǎo)致無法對(duì)這它們的區(qū)分。但是事實(shí)上,由MapViewOfFile()所返回得到的基地址只是文件映像在進(jìn)程地址空間中的起始基地址,因此在映射同一內(nèi)存映射文件的兩個(gè)相同映像時(shí)將會(huì)產(chǎn)生對(duì)內(nèi)存映射文件同一部分的兩個(gè)不同基地址的相同映像,可以用同樣的方法調(diào)用UnmapViewOfFile()將其從進(jìn)程的地址空間中予以撤消。

              5CloseHandle():Win32的大多數(shù)對(duì)象一樣,在使用完畢之后總是要通過CloseHandle()函數(shù)將已打開的內(nèi)核對(duì)象關(guān)閉。如果忘記關(guān)閉對(duì)象,在程序繼續(xù)運(yùn)行時(shí)將會(huì)出現(xiàn)資源泄漏。雖然在程序退出運(yùn)行時(shí),操作系統(tǒng)會(huì)自動(dòng)關(guān)閉在進(jìn)程中已經(jīng)打開但未關(guān)閉的任何對(duì)象。但是在進(jìn)程的運(yùn)行過程中,勢必會(huì)積累過多的資源句柄。因此在不再需要使用對(duì)象的時(shí)候通過CloseHandle()將其予以關(guān)閉是有意義的。

              小結(jié)

              本文對(duì)內(nèi)存映射文件在大文件處理中的應(yīng)用作了較為詳細(xì)的闡述。經(jīng)實(shí)際測試,內(nèi)存映射文件在處理大數(shù)據(jù)量文件時(shí)表現(xiàn)出了良好的性能,比通常使用CFile類和ReadFile()WriteFile()等函數(shù)的文件處理方式具有明顯的優(yōu)勢。本文所述程序代碼在Windows 2000 Professional下由Microsoft Visual C++ 6.0編譯通過。

            posted on 2008-12-26 16:27 肥仔 閱讀(3002) 評(píng)論(2)  編輯 收藏 引用 所屬分類: Windows開發(fā)

            評(píng)論

            # re: 內(nèi)存映射文件使用詳細(xì)(圖不錯(cuò))  回復(fù)  更多評(píng)論   

            CFileDialog dlg(TRUE,"sgy","*.sgy");
            if(dlg.DoModal() ==IDOK)
            {
            // DWORD filesize = dlg.GetFileName().GetLength();//.GetFile()->GetLength();
            m_sSegy_File_Name = dlg.GetPathName();
            hFile = CreateFile(m_sSegy_File_Name,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING, FILE_FLAG_RANDOM_ACCESS, NULL);
            if (hFile == INVALID_HANDLE_VALUE)
            {
            str.Format("Create File Object Error,Error Code:%d",GetLastError());
            ::MessageBox(NULL,str,"Error",MB_OK);
            return;
            }

            dwFileSizeLow = ::GetFileSize(hFile,&dwFileSizeHigh);//獲得文件大小,共64位

            dwFileSize = (ULONGLONG)((__int64)dwFileSizeLow | ((__int64)dwFileSizeHigh << 32));
            // str.Format("文件大小:%lu",dwFileSize);
            // ::MessageBox(NULL,str,"Error",MB_OK);

            hFileMapping = CreateFileMapping(hFile,NULL,PAGE_READONLY,dwFileSizeHigh,dwFileSizeLow,NULL);

            if (hFileMapping == NULL)
            {
            str.Format("創(chuàng)建文件映射對(duì)象失敗,錯(cuò)誤代碼:%drn:%d",GetLastError());
            ::MessageBox(NULL,str,"Error",MB_OK);
            return;
            }

            lpvoid = (LPBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,0,0,65536);


            // CopyMemory((PVOID)pBuf, szMsg, strlen(szMsg));
            // &m_cSegy_Head = (char *)lpvoid;
            // oldlpvoid = lpvoid;

            // memcpy(&m_cSegy_Head,(char *)lpvoid,3200);
            // lpvoid += 3200;
            // memcpy(&m_h400[0],(char *)lpvoid,sizeof(HEAD_BLOCK));
            // dwFileSizeLow |= (((__int64)dwFileSizeHigh) << 32);
            // m_h400[0].m_4_1 = ((m_h400[0].m_4_1 << 8) + ((m_h400[0].m_4_1 >> 8) & 0x0ff));
            // str.Format("Trace_Num = %d,Ext_Num=%d",m_h400[0].m_4_1,m_h400[0].m_4_2);
            // ::MessageBox(NULL,str,"GG",MB_OK);

            CRecordInfoDlg cc;

            cc.m_bExchange_Byte = m_bCheck_Swap_Byte;
            cc.m_nTrace_Num = ReturnValue(*(lpvoid + 3212),*(lpvoid + 3213));
            cc.m_nSample = ReturnValue(*(lpvoid + 3216),*(lpvoid + 3217));
            cc.m_nSample_Num = ReturnValue(*(lpvoid + 3220),*(lpvoid + 3221));
            cc.m_nSource_Num = 0;

            if(cc.DoModal() == IDOK)
            m_bCheck_Swap_Byte = cc.m_bExchange_Byte;

            m_nDataType = ReturnValue(*(lpvoid + 3224),*(lpvoid + 3225));//數(shù)據(jù)編碼格式

            short byte_num = 4;

            switch(m_nDataType)
            {
            case 1:
            {
            str.Format("數(shù)據(jù)采樣格式碼= %d ,為32位浮點(diǎn)數(shù)",m_nDataType);
            byte_num = 4;
            break;
            }
            case 2:
            {
            str.Format("數(shù)據(jù)采樣格式碼= %d ,為32位定點(diǎn)數(shù)",m_nDataType);
            byte_num = 4;
            break;
            }
            case 3:
            {
            str.Format("數(shù)據(jù)采樣格式碼= %d ,為16位定點(diǎn)數(shù)",m_nDataType);
            byte_num = 2;
            break;
            }
            case 4:
            {
            str.Format("數(shù)據(jù)采樣格式碼= %d ,為32位定點(diǎn)數(shù)W/增益碼",m_nDataType);
            byte_num = 4;
            break;
            }
            }

            ::MessageBox(NULL,str,"Message",MB_OK);

            ULONGLONG size = 3600;
            lpvoid += 3200;
            int num = 0;
            int i = 0;
            int Trace_Num = 1;
            int Old_Trace_Num = 1;//用來保存上一道的順序號(hào)
            __int64 FileOffset = 0;

            m_nSample_Num = ReturnValue(*(lpvoid + 20),*(lpvoid + 21));
            m_nSample = ReturnValue(*(lpvoid + 16),*(lpvoid + 17));

            CStdioFile file;
            file.Open("D:\\SGY.txt",CFile::modeCreate | CFile::modeReadWrite | CFile::typeText);
            lpvoid += 400;

            UnmapViewOfFile(lpvoid);

            SYSTEM_INFO SysInfo;
            GetSystemInfo(&SysInfo);
            DWORD dwGran = SysInfo.dwAllocationGranularity;

            do
            {
            if(size >= dwFileSize)
            break;

            FileOffset = 3600 + i * (240 + m_nSample_Num * byte_num);
            LPBYTE lpvoid1 = (LPBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,(DWORD)(FileOffset >> 32),(DWORD)(FileOffset & 0xFFFFFFFF),dwGran);//每次映射一道數(shù)據(jù)進(jìn)入內(nèi)存
            if (lpvoid1 == NULL)
            {
            TRACE("映射文件映射失敗,錯(cuò)誤代碼:%d ", GetLastError());
            return;
            }
            Trace_Num = ReturnValue(*(lpvoid + 12),*(lpvoid + 13),*(lpvoid + 14),*(lpvoid + 15));
            if(Trace_Num == 1)
            m_nSP_Start_Address[num] = lpvoid;//當(dāng)?shù)捞?hào)為1時(shí),則認(rèn)為是一炮的開始
            if(i != 0 && Trace_Num == 1)
            {
            m_nTrace_Num_Every_SP[num] = Old_Trace_Num;
            num ++;
            }

            str.Format("%lu %d %d\n",size,m_nTrace_Num_Every_SP[num],m_nSample_Num);
            size += (240 + m_nSample_Num * byte_num);
            file.WriteString(str);
            i++;
            UnmapViewOfFile(lpvoid1);
            }while(true);

            file.Close();

            m_check = true;
            ::MessageBox(NULL,str,"Message",MB_OK);

            }
            請(qǐng)問一下版主,為什么我執(zhí)行到 LPBYTE lpvoid1 = (LPBYTE)MapViewOfFile(hFileMapping,FILE_MAP_READ,(DWORD)(FileOffset >> 32),(DWORD)(FileOffset & 0xFFFFFFFF),dwGran);這一句時(shí),返回值都是0;我試過很多遍了,不論我打開的文件是大還是小,到這里都出錯(cuò)。
            謝謝!
            2009-02-22 21:18 | lzh

            # re: 內(nèi)存映射文件使用詳細(xì)(圖不錯(cuò))  回復(fù)  更多評(píng)論   

            我已經(jīng)解決了,是FileOffset必須為系統(tǒng)粒度的整數(shù)倍!
            2009-02-25 19:37 | lzh
            97久久精品无码一区二区天美| 99久久亚洲综合精品网站| 久久午夜无码鲁丝片秋霞| 国色天香久久久久久久小说| 少妇高潮惨叫久久久久久| 91精品国产综合久久香蕉| 久久久国产亚洲精品| 国产精品日韩深夜福利久久| 精品久久久无码21p发布| 99精品久久久久久久婷婷| 久久人人爽人人人人片av| 久久久不卡国产精品一区二区| 亚洲国产精品无码久久久不卡| 久久亚洲国产精品123区| 久久国产精品一国产精品金尊| 久久久这里有精品中文字幕| 91精品无码久久久久久五月天| 久久精品国产99国产精品导航| 久久精品国产欧美日韩| 97久久国产亚洲精品超碰热| 久久亚洲精品成人AV| 九九精品久久久久久噜噜| 久久中文精品无码中文字幕| 国产精品九九久久免费视频 | 97热久久免费频精品99| 99久久免费国产精品特黄| 精品国产91久久久久久久a | 99久久这里只精品国产免费| 欧美伊人久久大香线蕉综合69| 国内精品久久久久久久涩爱 | 久久久亚洲精品蜜桃臀| 久久久艹| 久久久一本精品99久久精品88| 午夜福利91久久福利| 深夜久久AAAAA级毛片免费看| 亚洲国产成人精品91久久久| 久久夜色精品国产www| 亚洲欧美国产日韩综合久久| 亚洲性久久久影院| 女人高潮久久久叫人喷水| 久久WWW免费人成一看片|