1. 程式人生 > >[翻譯,boost]共享記憶體_3_記憶體對映檔案

[翻譯,boost]共享記憶體_3_記憶體對映檔案

What is a memory mapped file ?

什麼是記憶體映像檔案 ?

File mapping is the association of a file's contents with a portion of the address space of a process.
檔案映像就是建立檔案中所包含的內容與程序記憶體地址中的部分空間二者之間的聯絡

The system creates a file mapping to associate the file and the address space of the process.
系統建立檔案映像的目的就是為了通過它建立檔案與程序地址空間的聯絡


A mapped regsion is the portion of address that the process uses to access the file's contents .
對映域是(程序)地址的一部分,通過對映域可以在程序中訪問(映像)檔案中的內容

A single file mapping can have several mapped regsions,so that the user can associate parts of the file with the address space of the process without 
mapping the entire file in the address space, since the file can be bigger than the whold address space of the process ( a 9GB DVD image file in a usual 32 bit systems).

就單個的映像檔案而言,它可以被對映到多個對映域中。這樣的話,使用者便可以將與之關聯的檔案段對映到其所在的程序地址空間中,而不是
把整個的映像檔案對映到地址空間中,因為映像檔案的大小有可能要遠比整個程序地址空間要大(程序地址空間的大小 在常見 32 位系統中有一個 9GB 的DVD 視訊檔案那麼大)

Processes read from and write to the file using pointers ,just like with dynamic memory .
同訪問動態分配而建立的記憶體空間的方法一樣,程序使用指標向映像檔案中讀取、寫入資料

File mapping has the following advantages:

使用映像檔案(來作為共享記憶體物件)有著如下的優點:

        Uniform resource use. File and memory can be treated using the same functions
        統一資源使用方式. 檔案與記憶體可以通過相同的方法來訪問.

        Automatic file data synchronization and cache from the OS.     
         作業系統會為檔案提供自發的資料同步策略以及檔案快取機制(這些無需自己程式設計實現)

        Reuse of C++ utilities( STL containers, algorithms) in files.
        在操作檔案物件的時候,可以重用 C++ 中的許多公共庫函式(比如 STL 容器庫,演算法庫都可以用在檔案物件上)

        Shared memory between two or more applications.
         (使用映像檔案作為共享記憶體物件的話)可以在兩個或更多的應用程序間建立共享記憶體.

        Allows efficient work with a large files, without mapping the whole file into memory
        不將全部的檔案對映到程序地址空間,這一特點提高將大檔案作為共享記憶體物件的工作效率

        If several processes use the same file mapping to create mapped regions of a file , each process' views contain identical copies of the file on disk.
        如果多個程序將同一個檔案對映到自己的程序地址空間並將其對映為對映域物件的話,(是不會將該檔案多次儲存的)每個程序所看到的全都是同一個來自於硬碟上的該映像檔案的唯一的拷貝.

File mapping is not only used for interprocess communication, it can be used also to simplify file usage, so the user does not need to use file-management functions
write to the file.
檔案映像不僅能用在程序間通訊方面,它還可以被用在簡化檔案操作方面,這樣一來使用者便無需通過使用檔案管理方法來向檔案中寫資料了.

The user just writes data to the process memory, and the operating systems dumps the data to the file.        
使用者僅可以通過將資料寫入到程序記憶體中,隨後作業系統便會將寫入的資料轉儲到檔案中.

When two processes map the same file in memory, the memory that one process writes is seen by another process, so memory mapped files can be used as an 
interprocess communication mechanism.
當兩個程序把同一個檔案對映到其記憶體地址空間中時,其中一個程序向共享記憶體寫入的資料對於另個一程序時可見的,所以建立記憶體映像檔案可以被用作程序間通訊
的一種機制

We can say that memory-mapped files offer the same interprocess communication services as shared memory with the addition of filesystem persistence.
我們可以得出這樣的結論: 記憶體映像檔案和共享檔案一樣可被用在程序通訊服務中,除此之外,記憶體映像檔案還具備有著與檔案系統同樣的生命週期, 正是因為這一點,
讓寫入到映像檔案中的資料可被持久化.

However,as the operating system has to synchronize the file contents with the memory contents, memory-mapped files are not as fast as shared memory.
但是,由於作業系統不得不同步化檔案與記憶體中的資料,這使得映像檔案記憶體的讀寫速度遠不及共享記憶體.

Using mapped files 
使用映像檔案

To use memory-mapped files, we have to perform 2 basic steps:
使用記憶體映像檔案,我們需要執行下面兩步基本的操作:

            Create a mappable object that represent an already created file of the fillesystem.
               建立可被對映的物件用來表示已經創建於檔案系統中的檔案

            This object will be used to create multiple mapped regions of the file.
              這個可被對映物件將被用作做,被多次對映到(不同程序中的) 由對映檔案建立的對映域

               Associate the whole file or parts of the file with the address space of the calling process.
               建立需要對映的程序中的地址空間與整個檔案或是部分檔案之間的對映關係

               The operating system looks for a big enough memory address range in the calling process' address space and marks that address range as an spacial range.
               作業系統會在所有申請對映程序的記憶體地址中找出一個足夠大的出來,然後將該地址區域標誌為特殊的記憶體地址域

              Changes in that address range are automatically seen by other process that also have mapped the same file and those changes are also transferred to the disk
               automatically.
               在特殊地址域中的數值變化對其他將相同檔案對映到自己地址域中的程序,是自動可見的,並且對該特殊地址域中數值的修改會被(作業系統)自動的轉儲到
               (該映像檔案所存放的)硬碟中。

Once the two steps have been successfully completed, the process can start writing to and reading from the address space to send to and receive data from other process
and synchronize the file's contents with the changes made to the mapped region.
這兩個步驟一經成功執行,程序便可開始從該地址空間中寫入傳送給其他程序的資料和讀出來自於其他程序的資料資訊了,同時還可以將對映域中資料的變化同步到
檔案中.

Now,let's see how can we do this using Boost.Interprocess.
好的,那麼我們來看看如何使用 Boost.Interprocess 函式庫來實現上述的情景

Header 
需要引入的標頭檔案

To manage mapped files, you just need to include the following header.
想要操作對映檔案,你僅需要把下面的標頭檔案引入到自己的程式碼檔案中.

#include 

Creating a file mapping
建立映像檔案

First , we have to link a file's contents with the process' address space. 
首先,我們需要做的就是建立檔案的內容與程序地址空間二者的連結關係

To do this, we have to create a mappable object that represents that file.
這麼做的話,我們就要建立一個用來代表該檔案的可對映的物件.

This is achieved in Boost.Interprocess creating a file_mapping object:
上述的這個功能在 Boost.Interprocess 庫中已經被實現了,只需要建立一個 file_mapping 的例項物件即可:

using boost::interprocess ;

file_mapping m_file ("/user/home/file " ,  // filename 這個便是所要建立的映像檔案的檔名稱
                                read_write ,            // read-write mode 這個是映像檔案允許被訪問的模式
                                ) ;

Now we can use the newly create object to create mapped regions.
現在我們就可以使用新建立的物件來生成對映域了

Mapping File's Contents In Memory
將檔案中的內容對映如記憶體中

After creating a file mapping, a process just has to map the shared memory in the process' address space.
在建立映像檔案之後,程序便可以將這個映像檔案作為共享記憶體物件對映到自己的程序地址空間中.

The user can map the whole shared memory or just part of it.
使用者(程序) 可以把整個或是其中的一部分檔案對映到自己的程序地址空間中.

The mapping process is done using the mapped_region class , as we have said before .
對映的過程, 如我們之前所說的一樣,通過建立mapped_region 類例項便可以實現

The class represents a memory region that has been mapped from a shared memory or from other devices that have also mapping capabilities:
mapped_region 這個類(抽象)表示的是: 從一個共享記憶體或是其他具有可對映性的裝置中提取出來的一塊記憶體區域

using boost::interprocess ;
std::size_t FileSize = ...

// Map the second half of the file
mapped_region region 
(
     m_file ,                         // Memory-mappable object   記憶體-可對映 物件
     read_write ,                   // Access mode                    訪問記憶體對映物件的模式
     FileSize / 2 ,                 // Offset from the beginning of the shm                   從記憶體-可對映物件起始位置多少位元組開始對映
     FileSize - FileSize/2  ,     // length of the region                                從記憶體-可對映 中對映多少個自己額
) ;

// Get the address of the region
// 該方法 get_address () 來自於 mapped_region 類,用來獲取對映域中的起始地址  
region.get_address () ;

// get the size of the region 
// get_size()  方法來自於 mapped_region 類,用於獲取對映域所對應的對映物件的位元組個數 
// 對應於 mapped_region 類構造方法中的第四個引數
region.get_size() ;

The user can specify the offset from the file where the mapped region should start and the size of the mapped region .
使用者可以通過設定 offset 和 size 引數來指定將映像檔案從哪裡開始對映到對映域中以及將映像檔案從起始位置開始向對映域中總共對映多少個位元組.

If no offset or size is specified , the whole file is mapped.
如果使用者沒有指定 offset 和 size 的數值,預設操作便是將整個檔案全部對映到對映域中

If the offset is specified,but not the size, the mapped region covers from the offset until the end of the file.
如果僅指定了 offset (起始位置) 但是沒有指定 size(總共設定多少個位元組), 對映域將從設定映像檔案的 起始位置開始將後續直到檔案結束的所有位元組
全部對映到對映域中.

If several processes map the same file, and a process modifies a memory range from a mapped region that is also mapped by other process, the changes are
immediately visible to other processes.
如果有多個程序將同樣的檔案對映到自己的地址空間中,其中一個程序通過對映域修改了記憶體中的內容,這段被修改的記憶體同樣也會被對映到其他的程序中,
改動的部分對剩餘的程序是立即可見的.(這種變動是實時可見的,不存在重新整理或是延遲時間)

However, the file contents on disk are not updated immediately, since that would hurt performance ( writing to disk is several times slower than writing to memory).
然而,記憶體中檔案內容的變動是不會立即更新到存放該檔案的硬碟上的,因為立即重新整理存放在硬碟檔案資料的話會影響到程式的效能(重新整理硬碟要比重新整理記憶體多花幾倍的時間)

If the user wants to make sure that file's contents have been updated,it can flush a range from the view to disk.
如果使用者像確保檔案內容的更新已經及時寫入到該檔案存放的硬碟上,它可以通過呼叫 flush 方法來將記憶體中某一區域上的資料,來將位於這一區間上的資料更新到磁碟上

// Flush the whole region
// 將整個對映域中變動的資料,重新整理到硬碟中
region.flush() ;

// Flush from an offset until the end of the region 
// 從 offset 標定的欄位開始直到 對映域結束 這一區間上的資料的變動 重新整理到硬碟中
region.flush(offset) ;

// Flush a memory range starting on an offset
// 通過指定起始點和位元組長度,來將這一區域上變動重新整理到硬碟上
// 變化域是 [offset, offset+size)
region.flush( offset, size);

Remember that the offset is not an offset on the file , but an offset in the mapped region.
需要謹記的是,flush 引數中的 offset 數值指的並不是 映像檔案中的起始地址 而是 對映域 中的起始地址

If a region covers the second half of a file and flushes the whole region, only the half of the file is guaranteed to have been flushed.
如果對映域中包含的是半個對映檔案,那麼執行對映域的整體重新整理操作的話,僅僅會使得硬碟上的映像檔案一半內容得到重新整理。

For more details regarding mapped_region see the boost::interprocess::mapped_region class reference.
想要知道 mapped_region 更多的細節資訊的話,參看 boost::interprocess::mapped_region 類參考資料

A Simple Example  
簡單例項

Let's reproduce the same example described in the shared memory section ,  using memory mapped files.
讓我們通過使用記憶體映像檔案來再次重新描述一下在 共享記憶體哪一張節中所實現的例子吧

A server process creates a shared memory segment, maps it and initializes all the bytes to a value .
服務程序建立了一個共享記憶體段,並將其對映到對映域中,並通過對映域物件將記憶體段中的所有位元組全部初始化成某一個數值

After that , a client process opens the shared memory, maps it , and checks that the data is correctly initialized :
在此之後,客戶程序開啟共享記憶體,將其對映到自己程序的地址空間中,並檢查共享記憶體段中的每個位元組是否被正確的初始化.

點選(此處)摺疊或開啟

  1. #include <boost/interprocess/file_mapping.hpp>
  2. #include <boost/interprocess/mapped_region.hpp>
  3. #include <iostream>
  4. #include <fstream>
  5. #include <string>
  6. #include <vector>
  7. #include <cstring>
  8. #include <cstddef>
  9. #include <cstdlib>
  10. int main(int argc, char *argv[])
  11. {
  12.    using namespace boost::interprocess;
  13.    //Define file names
  14.   // 首先設定檔名稱,建立檔案物件作為生成對映域
  15.   // 的共享記憶體物件
  16.    const char *FileName = "file.bin";
  17.    const std::size_t FileSize = 10000;
  18.    if(argc == 1){ //Parent process executes this
  19.       { //Create a file
  20.         // 下面的 if 分支下面的程式碼是 server 程序將要執行的
  21.        //
  22.        // 為了防止在 server 程序之前就有其他程序使用同名的檔案作為共享物件建立 對映域
  23.        // 造成此次建立對映域的錯誤,首先刪除同名檔案所建立的對映域
  24.          file_mapping::remove(FileName);
  25. //    
  26. //        隨後建立 filebuf 檔案緩衝物件,並通過檔案緩衝類中的 open 方法將檔案以
  27. // 讀模式寫模式,如果不能存在則建立同名檔案和二進位制檔案模式 開啟
  28.          std::filebuf fbuf;
  29.          fbuf.open(FileName, std::ios_base::in | std::ios_base::out
  30.                               | std::ios_base::trunc | std::ios_base::binary);
  31.          //Set the size
  32.          // 將檔案位移指標 以檔案的起始位置開始移動指向檔案中最後一個位元組上
  33.          fbuf.pubseekoff(FileSize-1, std::ios_base::beg);
  34.         // 將檔案中最後一個位元組的資料置為數值 0
  35.          fbuf.sputc(0);
  36.       }
  37.       //Remove on exit
  38.      // 建立封裝了 移除映像檔案操作的 結構體
  39.     // 結構體的建構函式 用於為結構體中的 FileName_ 變數進行賦值操作
  40.    // 而結構體的解構函式 用於將指定名稱的檔案對應的共享記憶體移除
  41.       struct file_remove
  42.       {
  43.          file_remove(const char *FileName)
  44.             : FileName_(FileName) {}
  45.          ~file_remove(){ file_mapping::remove(FileName_); }
  46.          const char *FileName_;
  47.       } remover(FileName);
  48.       //Create a file mapping
  49.       // 基於剛剛建立的映像檔案進行對映, m_file 便是基於該映像檔案生成的共享記憶體物件
  50.       file_mapping m_file(FileName, read_write);
  51.       //Map the whole file with read-write permissions in this process
  52.     // 在本程序中將全部的共享記憶體物件對映到對映域中
  53.       mapped_region region(m_file, read_write);
  54.       //Get the address of the mapped region
  55.      // 通過生成的對映域物件,可以獲取該共享記憶體的起始地址 和 該共享記憶體的大小(位元組)
  56.       void * addr = region.get_address();
  57.       std::size_t size = region.get_size();
  58.       //Write all the memory to 1
  59.       // 將共享記憶體中的所有位元組均置為數值 1
  60.       std::memset(addr, 1, size);
  61.       //Launch child process
  62.      // 接下來執行子程序,將執行命令從之前的 ./demo ---> 修改為 ./demo child 
  63.      // argc 從 1 ---> 2 導致 if(argc != 1 ) 從而走的是 else 分支
  64.       std::string s(argv[0]); s += " child ";
  65.       if(!=