ienki 发表于 2015-7-6 05:35:23

Mongodb源码分析--内存文件映射(MMAP)

在Mongodb中,其使用了操作系统底层提供的内存映射机制,即MMAP。MMAP可以把磁盘文件的一部分或全部内容直接映射到内存,这样文件中的信息位置就会在内存中有对应的地址空间,这时对文件的读写可以直接用指针来做,而不需要read/write函数了。同时操作系统会将数据刷新保存到磁盘上。如下图:
       http://daizhj.iyunv.com/images/cnblogs_com/daizhj/io.mmap.png  鉴于linux,window系统为mmap所提供的API大同小异(见下图)。这里仅以mongodb对window系统的mmap调用机制为例,来说明一下其具体的实现方式,以及在mongodb启动时,客户端提交查询和插入操作请求时mongodb的mmap执行流程。
    http://daizhj.iyunv.com/images/cnblogs_com/daizhj/mongodb_mmap_classdraw1.png
    上面类图中:
    MongoFile:定义了mongo文件对象常用操作,包括创建,关闭,设置名称,flushAll,获取MongoFile文件总尺寸等。
    MMF: 一个类型定义,其声明:typedef MemoryMappedFile MMF;   
    MongoMMF:为了便于journaling/durability操作,对MemoryMappedFile进行了一些封装(特别是对private views )
      
    下面着重看一下windows提供的mmap的常用API:
    MapViewOfFile(): 把文件数据映射到进程的地址空间
    CreateFileMapping() : 创建一个新的文件映射内核对象
    FlushViewOfFile(): 强制系统将内存中修改过的数据重新写入磁盘映像,从而可以确保所有的数据更新能及时保存到磁盘
    CloseHandle(): 关闭文件映射对象和文件对象
    MapViewOfFileEx(): 将文件映射到指定的进程地址空间
    参数说明:   



MapViewOfFile(
    __in HANDLE hFileMappingObject,/*hFileMappingObject是共享文件对象*/
    __in DWORD dwDesiredAccess, /*dwDesiredAccess是文件共享属性*/
    __in DWORD dwFileOffsetHigh, /*dwFileOffsetHigh是文件共享区的偏移地址*/
    __in DWORD dwFileOffsetLow, /*dwFileOffsetLow是文件共享区的偏移地址*/
    __in SIZE_T dwNumberOfBytesToMap /*dwNumberOfBytesToMap是共享数据长度*/
    );
  



//winbase.h
    CreateFileMappingW(
    __in      HANDLE hFile,   /*hFile是创建共享文件的句柄*/
    __in_opt LPSECURITY_ATTRIBUTES lpFileMappingAttributes, /*lpFileMappingAttributes是文件共享的属性*/
    __in      DWORD flProtect,/*flProtect是当文件映射时读写文件的属性*/
    __in      DWORD dwMaximumSizeHigh, /*是文件共享的大小高位字节*/
    __in      DWORD dwMaximumSizeLow, /*是文件共享的大小低位字节*/
    __in_opt LPCWSTR lpName /*lpName是共享文件对象名称*/
    );
    #ifdef UNICODE
    #define CreateFileMappingCreateFileMappingW
    #else
    #define CreateFileMappingCreateFileMappingA
    #endif // !UNICODE  



    FlushViewOfFile(
    __in LPCVOID lpBaseAddress, /*内存映射文件中的视图的一个字节的地址*/
    __in SIZE_T dwNumberOfBytesToFlush /*想要刷新的字节数*/
    );  



    MapViewOfFileEx(
    __in HANDLE hFileMappingObject,/*共享文件对象*/
    __in DWORD dwDesiredAccess, /*文件共享属性*/
    __in DWORD dwFileOffsetHigh, /*文件共享区的偏移地址*/
    __in DWORD dwFileOffsetLow, /*文件共享区的偏移地址*/
    __in SIZE_T dwNumberOfBytesToMap /*共享数据长度*/
    __in_opt LPVOID lpBaseAddress /*指定映射文件映射对象的地址。如这个地址处没有足够的内存空间,
                                    那么对MapViewOfFileEx的调用会失效*/
    );  
    下面我们看一下mongodb如何使用上述API,来实现windows环境下对mongofile进行mmap操作的.
   



//mmap_win.cpp
   mutex mapViewMutex("mapView");//声明mapView的互斥体(mutex)对象
    ourbitset writable;
    /** unmapping 通知,以便清空 writable bits */
    void MemoryMappedFile::clearWritableBits(void *p) {
      for( unsigned i = ((size_t)p)/ChunkSize; i createExtent(ns, approxSize, newCapped, loops+1);
      .....
    }
  
  最后在addAFile方法中,我们会看下如下代码段:
   



//database.cpp
    MongoDataFile* Database::addAFile( int sizeNeeded, bool preallocateNextFile ) {
      int n = (int) files.size();
      MongoDataFile *ret = getFile( n, sizeNeeded );//调用下面的getFile方法
      .....
    }
    //database.cpp
    MongoDataFile* Database::getFile( int n, int sizeNeeded , bool preallocateOnly) {
      ......
      namespaceIndex.init();
      .....
    }
   
    //namespace.cpp
    void NamespaceIndex::init() {
      ......
      unsigned long long len = 0;
      boost::filesystem::path nsPath = path();
      string pathString = nsPath.string();
      void *p = 0;
      if( MMF::exists(nsPath) ) {//使用本文前面提到的MMF类,判断数据库文件是否存在
            if( f.open(pathString, true) ) {//打开指定的文件并执行mmap操作
                len = f.length();
                if ( len % (1024*1024) != 0 ) {
                  log()
页: [1]
查看完整版本: Mongodb源码分析--内存文件映射(MMAP)