Boost.Interprocess.file_mapping内存映射文件

什么是内存映射文件?
文件映射是文件内容与进程地址空间的一部分的关联。系统创建一个文件映射关联文件和进程的地址空间。映射区域是进程用来访问文件内容的地址空间的一部分。一个文件映射可以有多个映射区域,以便用户可以将文件的各个部分与进程的地址空间关联起来,而不必将整个文件映射到地址空间中,因为文件可以大于整个地址空间过程(通常的32位系统中的9GB DVD映像文件)。使用指针读取和写入文件的过程就像动态内存一样。


统一的资源使用。文件和内存可以使用相同的功能进行处理。
自动文件数据同步和来自操作系统的缓存。
在文件中重用C ++实用程序(STL容器,算法)。
两个或多个应用程序之间共享内存。
允许使用大文件进行高效的工作,而无需将整个文件映射到内存中
如果多个进程使用相同的文件映射来创建文件的映射区域,则每个进程的视图都包含磁盘上文件的相同副本。
文件映射不仅用于进程间通信,还可以用来简化文件的使用,所以用户不需要使用文件管理功能来编写文件。用户只是将数据写入进程内存,操作系统将数据转储到文件中。

当两个进程映射内存中的同一文件时,一个进程写入的内存被另一个进程看到,因此内存映射文件可以用作进程间通信机制。我们可以说内存映射文件提供了与共享内存相同的进程间通信服务,并增加了文件系统持久性。但是,由于操作系统必须将文件内容与存储器内容同步,所以内存映射文件不如共享内存快。

使用映射文件
要使用内存映射文件,我们必须执行2个基本步骤:

创建一个表示已经创建的文件系统文件的可映射对象。该对象将用于创建文件的多个映射区域。
将整个文件或部分文件与调用进程的地址空间相关联。操作系统在调用进程的地址空间中查找足够大的内存地址范围,并将地址范围标记为特殊范围。其他进程也会自动查看该地址范围内的更改,这些进程也映射了相同的文件,并且这些更改也会自动传输到磁盘。
一旦这两个步骤成功完成,进程就可以开始写入和读取地址空间,以发送和接收来自其他进程的数据,并将文件的内容与对映射区域所做的更改同步。现在,让我们看看如何使用Boost.Interprocess来做到这一点:

要管理映射文件,只需要包含以下标题:

#include  < boost / interprocess / file_mapping 。hpp >
创建一个文件映射
首先,我们必须将文件的内容与进程的地址空间相链接。为此,我们必须创建一个表示该文件的可映射对象。这是在Boost.Interprocess中 创建一个file_mapping 对象来实现的:

使用 boost :: interprocess ; 
file_mapping  m_file 
   (“/ usr / home / file”        //文件名
   ,read_write              //读写模式
   );
现在我们可以使用新创建的对象来创建映射区域。有关此类的更多详细信息,请参阅boost::interprocess::file_mapping 类参考。

在内存中映射文件的内容
创建文件映射后,进程只需在进程的地址空间中映射共享内存。用户可以映射整个共享内存或者只是其中的一部分。映射过程使用mapped_region类来完成。正如我们之前所说的那样,这个类表示一个已经从共享内存映射的内存区域,或者从其他具有映射功能的设备映射的内存区域:


使用 boost :: interprocess ; 
std :: size_t  FileSize  =  ...


//地图文件的第二半
mapped_region  区域
   ( m_file                    //内存可映射对象
   , READ_WRITE                //访问模式
   , 文件大小/ 2                //从SHM的开始偏移
   , 文件大小- 文件大小/ 2       的区域的长度// 
   ) ;


//获取区域的地址
区域。get_address ();


//获取区域的大小
的区域。get_size ();
用户可以指定从映射区域应该开始的文件的偏移量和映射区域的大小。如果未指定偏移量或大小,则会映射整个文件。如果指定了偏移量,但不是大小,则映射区域将覆盖直到文件结尾的偏移量。


如果多个进程映射相同的文件,并且进程从其他进程映射的映射区域修改内存范围,则这些更改对其他进程是可见的。但是,磁盘上的文件内容不会立即更新,因为这会损害性能(写入磁盘比写入内存要慢几倍)。如果用户想要确保文件的内容已经被更新,它可以刷新从视图到磁盘的范围。当函数返回时,刷新过程已经启动,但不能保证所有的数据都被写入磁盘:


//刷新整个区域的
区域。flush ();


//从直到区域的端部的偏移冲洗
区域。冲洗(偏移);


//从一个偏移量
区域开始刷新一个内存区域。齐平(偏移, 大小);
请记住,偏移量不是文件上的偏移量,而是映射区域中的偏移量。如果一个区域覆盖了文件的后半部分并刷新了整个区域,则只能保证文件的一半被刷新。


有关更多详细信息,mapped_region 请参阅boost::interprocess::mapped_region 类参考。


一个简单的例子
让我们使用内存映射文件重现共享内存部分中描述的相同示例。服务器进程创建一个共享内存段,映射它并将所有字节初始化为一个值。之后,客户端进程打开共享内存,映射它,并检查数据是否正确初始化::


#include  < boost / interprocess / file_mapping 。hpp > 
#include  < boost / interprocess / mapped_region 。hpp > 
#include  < iostream > 
#include  < fstream > 
#include  < string > 
#include  < vector > 
#include  < cstring > 
#include  < cstddef > 
#include  < cstdlib >


int  main (int  argc , char  * argv [])

   using  namespace  boost :: interprocess ;


   //定义文件名
   const  char  * FileName   =  “file.bin” ; 
   const  std :: size_t  FileSize  =  10000 ;


   if (argc  ==  1 ){  //父进程执行这个
      {   //创建文件
         file_mapping :: remove (FileName ); 
         std :: filebuf  fbuf ; 
         fbuf 。打开(FileName , std :: ios_base :: in  |  std :: ios_base :: out 
                              |  std :: ios_base :: trunc  |  std :: ios_base :: binary );
         //设置大小
         fbuf 。pubseekoff (FileSize - 1 , std :: ios_base :: beg ); 
         fbuf 。sputc (0 ); 
      }


      //在退出删除
      结构 file_remove 
      { 
         file_remove (const的 字符 * 文件名)
            : FileName_ (文件名) {} 
         〜file_remove (){  file_mapping :: 除去(FileName_ );  } 
         const  char  * FileName_ ; 
      }  卸妆(FileName );


      //创建文件映射
      file_mapping  m_file (文件名, READ_WRITE );


      //地图在这个过程中读-写权限整个文件
      mapped_region  区域(m_file , READ_WRITE );


      //获取映射区域的地址
      void  *  addr        =  region 。get_address (); 
      std :: size_t  size   =  region 。get_size ();


      //将所有内存写入1 
      std :: memset (addr , 1 , size );


      //启动子进程
      std :: string  s (argv [ 0 ]);  s  + =  “孩子” ; 
      如果(0  !=  STD :: 系统(小号。c_str ()))
         返回 1 ; 
   } 
   else {   //子进程执行this 
      {   //打开文件映射并将其映射为只读
         file_mapping  m_file (FileName , read_only );


         mapped_region  区域(m_file , read_only );


         //获取映射区域的地址
         void  *  addr        =  region 。get_address (); 
         std :: size_t  size   =  region 。get_size ();


         //检查内存初始化为1 
         const  char  * mem  =  static_cast < char *>(addr ); 
         for (std :: size_t  i  =  0 ;  i  <  size ;  ++ i )
            if (* mem ++  !=  1 )
               return  1 ;    //错误检查内存
      } 
      {   //现在测试它读取文件
         std :: filebuf  fbuf ; 
         fbuf 。打开(FileName , std :: ios_base :: in  |  std :: ios_base :: binary );


         //将其读入内存
         std :: vector < char >  vect (FileSize , 0 ); 
         fbuf 。sgetn (VECT [ 0 ], STD :: streamsize可(VECT 。大小()));


         //检查内存初始化为1 
         const  char  * mem  =  static_cast < char *>(&vect [ 0 ]); 
         for (std :: size_t  i  =  0 ;  i  <  FileSize ;  ++ i )
            if (* mem ++  !=  1 )
               return  1 ;    //检查内存时出错
      } 
   }


   返回 0 ; 
}

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道格拉斯范朋克

播种花生牛奶自留田

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值