1. 程式人生 > >檔案物件及檔案對映物件

檔案物件及檔案對映物件

檔案物件和檔案對映物件

(問題來源:IPP例子simple_player vm_mmap _win32.c檔案中的vm_mmap_create函式)

1.記憶體對映檔案   
      記憶體對映檔案與虛擬記憶體有些類似,通過記憶體對映檔案可以保留一個地址空間的區域,同時將物理儲存器提交給此區域,只是記憶體檔案對映的物理儲存器來自一個已經存在於磁碟上的檔案,而非系統的頁檔案,而且在對該檔案進行操作之前必須首先對檔案進行對映,就如同將整個檔案從磁碟載入到記憶體。由此可以看出,使用記憶體對映檔案處理儲存於磁碟上的檔案時,將不必再對檔案執行I/O操作,這意味著在對檔案進行處理時將不必再為檔案申請並分配快取,所有的檔案快取操作均由系統直接管理【把磁碟當記憶體,有個記憶體對映,從磁碟對映到記憶體,以後檔案的管理相當於是系統管理記憶體,那麼說多寫就是讀寫記憶體了,指標即可??

】,由於取消了將檔案資料載入到記憶體、資料從記憶體到檔案的回寫以及釋放記憶體塊等步驟,使得記憶體對映檔案在處理大資料量的檔案時能起到相當重要的作用。另外,實際工程中的系統往往需要在多個程序之間共享資料,如果資料量小,處理方法是靈活多變的,如果共享資料容量巨大,那麼就需要藉助於記憶體對映檔案來進行。實際上,記憶體對映檔案正是解決本地多個程序間資料共享的最有效方法。 【這麼說視訊編解碼中的頻繁讀取較大的視訊檔案是不是也可以用到這個??】  
      記憶體對映檔案並不是簡單的檔案I/O操作,實際用到了Windows的核心程式設計技術--記憶體管理。所以,如果想對記憶體對映檔案有更深刻的認識,必須對Windows作業系統的記憶體管理機制【參見windows核心程式設計Chapt13】有清楚的認識,記憶體管理的相關知識非常複雜,超出了本文的討論範疇,在此就不再贅述,感興趣的讀者可以參閱其他相關書籍。

2.記憶體對映檔案的一般方法:   
     首先要通過CreateFile()函式來建立或開啟一個檔案核心物件,這個物件標識了磁碟上將要用作記憶體對映檔案的檔案。在用CreateFile()將檔案映像在物理儲存器的位置通告給作業系統後,只指定了映像檔案的路徑,映像的長度還沒有指定。為了指定檔案對映物件需要多大的物理儲存空間還需要通過CreateFileMapping()函式來建立一個檔案對映核心物件以告訴系統檔案的尺寸以及訪問檔案的方式。在建立了檔案對映物件後,還必須為檔案資料保留一個地址空間區域【不是實際的實體記憶體,分配個地址,但是實際是對應磁碟上的位置】,並把檔案資料作為對映到該區域的物理儲存器進行提交。由MapViewOfFile()函式負責通過系統的管理而將檔案對映物件的全部或部分對映到程序地址空間。此時,對記憶體對映檔案的使用和處理同通常載入到記憶體中的檔案資料的處理方式基本一樣,在完成了對記憶體對映檔案的使用時,還要通過一系列的操作完成對其的清除和使用過資源的釋放。這部分相對比較簡單,可以通過UnmapViewOfFile()完成從程序的地址空間撤消檔案資料的映像、通過CloseHandle()關閉前面建立的檔案對映物件和檔案物件。   
3.記憶體對映檔案相關函式

   

     在使用記憶體對映檔案時,所使用的API函式主要就是前面提到過的那幾個函式,下面分別對其進行介紹:   

(1).CreateFile():This function creates, opens, or truncates a file, COM port, device, service, or console. It returns a handle that you can use to access the object. 

HANDLE CreateFile(
  LPCTSTR lpFileName,
  DWORD dwDesiredAccess,
  DWORD dwShareMode,
  LPSECURITY_ATTRIBUTES lpSecurityAttributes,
  DWORD dwCreationDisposition,
  DWORD dwFlagsAndAttributes,
  HANDLE hTemplateFile
); 

函式CreateFile()即使是在普通的檔案操作時也經常用來建立、開啟檔案,在處理記憶體對映檔案時,該函式來建立/開啟一個檔案核心物件,並將其控制代碼返回,在呼叫該函式時需要根據是否需要資料讀寫和檔案的共享方式來設定引數dwDesiredAccess和dwShareMode,錯誤的引數設定將會導致相應操作時的失敗。

(2).CreateFileMapping():This function creates a named or unnamed file-mapping object for the specified file

  HANDLE CreateFileMapping(
  HANDLE hFile,
  LPSECURITY_ATTRIBUTES lpFileMappingAttributes,
  DWORD flProtect,
  DWORD dwMaximumSizeHigh,
  DWORD dwMaximumSizeLow,
  LPCTSTR lpName
);   

CreateFileMapping()函式建立一個檔案對映核心物件,通過引數hFile指定待對映到程序地址空間的檔案控制代碼(該控制代碼由CreateFile()函式的返回值獲取)。由於記憶體對映檔案的物理儲存器實際是儲存於磁碟上的一個檔案,而不是從系統的頁檔案中分配的記憶體,所以系統不會主動為其保留地址空間區域,也不會自動將檔案的儲存空間對映到該區域,為了讓系統能夠確定對頁面採取何種保護屬性,需要通過引數flProtect來設定,保護屬性PAGE_READONLY、PAGE_READWRITE和PAGE_WRITECOPY分別表示檔案對映物件被對映後,可以讀取、讀寫檔案資料。在使用PAGE_READONLY時,必須確保CreateFile()採用的是GENERIC_READ引數;PAGE_READWRITE則要求CreateFile()採用的是GENERIC_READ|GENERIC_WRITE引數;至於屬性PAGE_WRITECOPY則只需要確保CreateFile()採用了GENERIC_READ和GENERIC_WRITE其中之一即可。DWORD型的引數dwMaximumSizeHigh和dwMaximumSizeLow也是相當重要的,指定了檔案的最大位元組數,由於這兩個引數共64位,因此所支援的最大檔案長度為16EB,幾乎可以滿足任何大資料量檔案處理場合的要求。

(3).MapViewOfFile():This function maps a view of a file into the address space of the  calling process.

LPVOID MapViewOfFile(
  HANDLE hFileMappingObject,
  DWORD dwDesiredAccess,
  DWORD dwFileOffsetHigh,
  DWORD dwFileOffsetLow,
  DWORD dwNumberOfBytesToMap
);

MapViewOfFile()函式負責把檔案資料對映到程序的地址空間,引數hFileMappingObject為CreateFileMapping()返回的檔案映像物件控制代碼。引數dwDesiredAccess則再次指定了對檔案資料的訪問方式,而且同樣要與CreateFileMapping()函式所設定的保護屬性相匹配。雖然這裡一再對保護屬性進行重複設定看似多餘,但卻可以使應用程式能更多的對資料的保護屬性實行有效控制。MapViewOfFile()函式允許全部或部分對映檔案,在對映時,需要指定資料檔案的偏移地址以及待對映的長度。其中,檔案的偏移地址由DWORD型的引數dwFileOffsetHigh和dwFileOffsetLow組成的64位值來指定,而且必須是作業系統的分配粒度的整數倍,對於Windows作業系統,分配粒度固定為64KB。當然,也可以通過如下程式碼來動態獲取當前作業系統的分配粒度:

  SYSTEM_INFO   sinf;  
  GetSystemInfo(&sinf);  
  DWORD   dwAllocationGranularity   =   sinf.dwAllocationGranularity;

引數dwNumberOfBytesToMap指定了資料檔案的對映長度,這裡需要特別指出的是,對於Windows   9x作業系統,如果MapViewOfFile()無法找到足夠大的區域來存放整個檔案對映物件,將返回空值(NULL);但是在Windows   2000下,MapViewOfFile()只需要為必要的檢視找到足夠大的一個區域即可,而無須考慮整個檔案對映物件的大小。

      在完成對對映到程序地址空間區域的檔案處理後,需要通過函式UnmapViewOfFile()完成對檔案資料映像的釋放,該函式原型宣告如下: BOOL   UnmapViewOfFile(LPCVOID   lpBaseAddress);     

   唯一的引數lpBaseAddress指定了返回區域的基地址,必須將其設定為MapViewOfFile()的返回值。在使用了函式MapViewOfFile()之後,必須要有對應的UnmapViewOfFile()呼叫,否則在程序終止之前,保留的區域將無法釋放。除此之外,前面還曾由CreateFile()和CreateFileMapping()函式建立過檔案核心物件和檔案對映核心物件,在程序終止之前有必要通過CloseHandle()將其釋放,否則將會出現資源洩漏的問題。

除了前面這些必須的API函式之外,在使用記憶體對映檔案時還要根據情況來選用其他一些輔助函式。例如,在使用記憶體對映檔案時,為了提高速度,系統將檔案的資料頁面進行快取記憶體,而且在處理檔案對映檢視時不立即更新檔案的磁碟映像。為解決這個問題可以考慮使用FlushViewOfFile()函式,該函式強制系統將修改過的資料部分或全部重新寫入磁碟映像,從而可以確保所有的資料更新能及時儲存到磁碟。

以下是一篇感覺有用的文章,提供參考:

對映檔案的第一步是呼叫CreateFile函式來開啟這個檔案。為了確保被對映的檔案不能被其他的程序寫入,你應該使用專門的許可權開啟這個檔案。另外,檔案控制代碼要一直開啟,直到程序不再需要它。一個得到專門的許可權的簡單方法是,將CreateFile的fdwShareMode引數設定為零。CreateFileMapping函式使用CreateFile函式返回的控制代碼來建立一個檔案對映物件。
   CreateFileMapping函式給檔案對映物件返回一個控制代碼。當建立檔案檢視的時候會用到這個控制代碼,使得你可以訪問共享的記憶體。當你呼叫CreateFileMapping函式時,你要指定一個物件名(控制代碼,也就是開啟的那個待對映檔案),從檔案中對映多少位元組,以及對映記憶體的讀寫許可。第一個呼叫CreateFileMapping函式的程序會建立一個檔案對映物件。如果檔案映像已經存在,程序呼叫CreateFileMapping函式函式會得到這個檔案映像物件的控制代碼。呼叫GetLastError函式,你就可以知道呼叫CreateFileMapping函式來建立或者開啟一個檔案對映物件是否成功了。GetLastError給建立的程序返回NO_ERROR,給之後的程序返回ERROR_ALREADY_EXISTS。
    如果CreateFileMapping函式中的許可權標誌和CreateFile函式中的許可權標誌不一致,則會執行失敗。舉個例子,讀寫一個檔案:
    1、給CreateFile函式的fdwAccess引數附GENERIC_READ和GENERIC_WRITE。
    2、給CreateFileMapping函式的fdwProtect引數附PAGE_READWRITE。
    建立一個檔案對映物件並不開闢實體記憶體,只是預定。

檔案對映大小
    檔案對映物件的大小和對映的檔案大小沒有關係。但是,如果檔案對映物件比檔案要大,系統會在CreateFileMapping返回之前,擴大檔案。如果檔案對映物件比檔案小,系統只從檔案中對映指定的位元組數。
   CreateFileMapping函式dwMaximumSizeHigh和dwMaximumSizeLow引數是你可以指定從檔案中對映的位元組數:
    1、如果你確實不想改變檔案的大小(比如,對映一個只讀檔案時),呼叫CreateFileMapping函式並將dwMaximumSizeHigh和dwMaximumSizeLow引數設定為零。這樣一來,檔案對映物件的大小和檔案的大小是一致的。否則,你應該估算一下已經完成的檔案的大小,因為檔案對映物件的大小是固定的;一旦被建立,他們的大小就不能被增大或減小。如果用這樣的方式來對映一個長度為零的檔案會引起ERROR_FILE_INVALID錯誤。程式應該檢驗檔案的長度,並且拒絕長度為零的檔案:
    2、以檔案支援的檔案對映物件的大小是受磁碟大小限制的。檔案檢視的大小,受最大的、未被預約的連續虛擬記憶體限制。這最多是2G減去程序已經預約的虛擬記憶體。
    你選擇的檔案對映大小,控制了你利用記憶體對映能“看到”的檔案的範圍。如果你建立了一個500Kb大小的檔案對映物件,不管檔案有多大,你只可以訪問檔案的前500Kb。既然建立更大的檔案對映物件不會消耗系統資源,建立和檔案一樣大的對映物件(將CreateFileMapping的dwMaximumSizeHigh和dwMaximumSizeLow引數設定為零),及時你不需要檢視整個檔案。當建立檔案檢視以及訪問他們的時候,才會消耗系統資源。
    加入你想要訪問的部分不是從檔案開頭開始的,你就必須建立一個檔案對映物件。物件的大小是你想要訪問的那一部分的大小加上它在檔案中的偏移量。

相關推薦

檔案物件檔案對映物件

檔案物件和檔案對映物件 (問題來源:IPP例子simple_player vm_mmap _win32.c檔案中的vm_mmap_create函式) 1.記憶體對映檔案         記憶體對映檔案與虛擬記憶體有些類似,通過記憶體對映檔案可以保留一個地址空間的區域,同時將物

檔案系統檔案許可權

檔案系統及檔案許可權 命令幫助 –help和-h選項 顯示用法總結和引數列表 使用的大多數,但並非所有的 示例: date–help Usage:date[OPTION]…[+FORMAT]or: date[-u|–utc|–universal][MMDDhhmm

Python之路【第五篇】:面向物件相關 面向物件基礎

其他相關 一、isinstance(obj, cls)  檢查是否obj是否是類 cls 的物件 1 2

HTML-JavaScript物件初識面向物件

物件是什麼 物件是包含相關屬性和方法的集合體(屬性)(方法) 什麼是面向物件 1.面向物件僅僅是一個概念或者程式設計思想 2.通過一種叫做原型的方式來實現面向物件程式設計 JavaScript中的基本資料型別: number(數值型別) string(字串型

JavaScript物件初識面向物件

自定義物件   基於Object物件的方式建立物件     var 物件名稱=new Object( ); var flower=new Object();     flower.name="長春花";     flower.genera="夾竹桃科 長春花屬";    

.Neter玩轉Linux系列之二:Linux下的檔案目錄檔案目錄的許可權

基礎篇 實戰篇 一、Linux下的檔案目錄 簡介:linux的檔案系統是採用級層式的樹狀目錄結構,在此 結構中的最上層是根目錄“/”,然後在此目錄下再建立 其他的目錄。深刻理解linux檔案目錄是非常重要的,如下圖所示: 將來你用哪個使用者登入,你就會在那個使用

pcap檔案格式檔案解析

第一部分:PCAP包檔案格式 一 基本格式:    檔案頭 資料包頭資料報資料包頭資料報...... 二、檔案頭:    檔案頭結構體 sturct pcap_file_header {      DWORD           magic;      DWORD     

Linux下獲得檔案屬性檔案結構體的使用

第一種,通過路徑的方法 int stat(const char *path, struct stat *_stat); int lstat(const char *path,struct stat *_stat); 兩者的第一個引數都是檔案的路徑,第二

python中寫入檔案資料檔案定位操作命令

如果我們向檔案中寫入資料的話,我們可以使用write()函式。 寫檔案: #開啟檔案 f = open("./index.cpp","w") #1. 寫入資料 contents ="hello w

Linux檔案系統檔案相關命令

Linux中一切皆檔案 檔案系統是作業系統用於在磁碟或分割槽上的組織檔案的方法和資料結構。 檔案系統由三部分組成:與檔案管理相關的軟體,被管理檔案以及實施檔案管理所需的資料結構。 從系統的角度來看,檔案系統是對檔案儲存器空間進行組織和分

Windows Server 2008 R2應用基礎-實驗二-檔案許可權檔案共享

實驗要求 實驗內容及步驟 掌握 NTFS 與 FAT32 檔案系統的區別,觀察各分割槽的檔案系統 比較同同一分割槽和不同 NTFS 分割槽上進行檔案複製、移動時檔案許可權的變化 設定檔案加密、壓縮 在 NTFS 分割槽建立資料夾 temp,進入屬性頁面中的“安

讓你提前認識軟體開發(12):配置檔案讀取檔案操作

第1部分 重新認識C語言配置檔案讀取及檔案操作【文章摘要】        在通訊領域的軟體開發專案中,C語言是主流的程式語言,而檔案操作在其中又佔有很重要的地位。此外,為了體現產品的靈活性,可新增配置檔

Linux系統檔案系統檔案基礎篇

Linux系統檔案系統及檔案基礎篇         學習L

Linux的檔案系統檔案快取知識點整理

https://www.luozhiyun.com/archives/291 ## Linux的檔案系統 ### 檔案系統的特點 1. 檔案系統要有嚴格的組織形式,使得檔案能夠以塊為單位進行儲存。 2. 檔案系統中也要有索引區,用來方便查詢一個檔案分成的多個塊都存放在了什麼位置。 3. 如果檔案系統中有的檔

【Spring】例項化上下文物件載入多個配置檔案

一、例項化上下文物件 ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); Car car = (Car) ctx.getBean("car");

使用jaxb 轉換bo物件XML檔案

bo物件: 注: jaxb只會序列化 public 的方法 ,若屬性宣告為public,且宣告 @XmlElement(name = "name") ,則需要在該屬性get方法上宣告@XmlTransient,表示不將get方法序列化成xml元素,否則會報重複屬性序列化。或

使用HttpClient傳送InputStream物件傳送檔案

最近在做的東西中有這樣一個需求要把一個檔案上傳到伺服器A上,再由伺服器A上傳到伺服器B上,而伺服器A上傳到伺服器B的這個請求是通過HttpClient傳送的。如果是傳送檔案的話很好辦,但是現在問題的難點是伺服器A通過HttpClient傳送的不是一個檔案,而是一個Input

遞迴計算指定目錄下的檔案檔案總個數,並封裝為ztree物件資料

最近參與的專案要求將指定目錄下的檔案及檔案個數通過遞迴演算法計算出來,並封裝轉換為前端ztree格式物件顯示結果。後臺主要邏輯實現,提供資料和資料模型,前端轉換封裝需要的物件資料格式。部分程式碼下面貼出來,僅供參考,請多指正! 一、controller程式碼: L

檔案物件檔案對映物件

1.記憶體對映檔案         記憶體對映檔案與虛擬記憶體有些類似,通過記憶體對映檔案可以保留一個地址空間的區域,同時將物理儲存器提交給此區域,只是記憶體檔案對映的物理儲存器來自一個已經存在於磁碟上的檔案,而非系統的頁檔案,而且在對該檔案進行操作之前必須首先對檔案進行

Python核心物件型別之元組檔案

元組 元組是不可變型別,以()表示,是任意物件的有序集合,同樣是序列的一種,index和count方法分別是取元素,統計元素個數。 語法比如(2,3)就是一個元組。元組與列表如此類似,為何需要重複的型別,主要它提供了不變性,提供了”常數”宣告。元組項雖不能修