mmap原理及其在ART中的应用(1)

mmap原理及其在ART中的应用(1)

什么是mmap

对于mmap,大家应该都很熟悉,是一个将文件映象到内存里,从此以后就可以直接读写内存,不用再通过文件访问的方式再去做流式操作的系统调用. 这样,也可以让文件映射的内存被多个进程共享,不用每个文件都共享一份。

mmap的声明

mmap调用定义于/usr/include/sys/mman.h中,原型如下:

extern void* mmap(void*, size_t, int, int, int, off_t);
extern void* mmap64(void*, size_t, int, int, int, off64_t);
extern int munmap(void*, size_t);

我们看下带详细说明的版本:

/* Map addresses starting near ADDR and extending for LEN bytes.  from
   OFFSET into the file FD describes according to PROT and FLAGS.  If ADDR
   is nonzero, it is the desired mapping address.  If the MAP_FIXED bit is
   set in FLAGS, the mapping will be at ADDR exactly (which must be
   page-aligned); otherwise the system chooses a convenient nearby address.
   The return value is the actual mapping address chosen or MAP_FAILED
   for errors (in which case `errno' is set).  A successful `mmap' call
   deallocates any previous mapping for the affected region.  */
extern void *mmap (void *__addr, size_t __len, int __prot,int __flags, int __fd, off_t __offset);
extern void *mmap64 (void *__addr, size_t __len, int __prot,int __flags, int __fd, off64_t __offset);
/* Deallocate any mapping for the region starting at ADDR and extending LEN
76   bytes.  Returns 0 if successful, -1 for errors (and sets errno).  */
extern int munmap (void *__addr, size_t __len);

地址,长度,偏移量,文件ID都容易理解,复杂的参数只有两个,prot和flags这两项。

内存保护标志

prot:内存保护标志,是否允许读,写,执行,不能与文件的打开模式冲突。
可取的值如下:
* PROT_EXEC:可执行
* PROT_READ:可读
* PROT_WRITE:可写
* PROT_NONE:不可访问

以上4个在mmap的man page中有说明。还有一个虽然man中没有说明,但是也可以使用:
* PROT_SEM: 用于原子操作

这几个宏定义于/usr/include/asm-generic/mman-common.h中:

#define PROT_READ   0x1     /* page can be read */
#define PROT_WRITE  0x2     /* page can be written */
#define PROT_EXEC   0x4     /* page can be executed */
#define PROT_SEM    0x8     /* page may be used for atomic ops */
#define PROT_NONE   0x0     /* page can not be accessed */
*/

映射类型和标志

需要注意的是,映射类型的标志,有位于mman-common.h中的,也有位于mman.h中的,移植的时候请注意兼容性。

下面这几个通用的,定义于/usr/include/asm-generic/mman-common.h中:

#define MAP_SHARED  0x01        /* Share changes */
#define MAP_PRIVATE 0x02        /* Changes are private */
#define MAP_TYPE    0x0f        /* Mask for type of mapping */
#define MAP_FIXED   0x10        /* Interpret addr exactly */
#define MAP_ANONYMOUS   0x20        /* don't use a file */

还有一些扩展的参数,定义于/usr/include/asm-generic/mman.h中:

#define MAP_GROWSDOWN   0x0100      /* stack-like segment */
#define MAP_DENYWRITE   0x0800      /* ETXTBSY */
#define MAP_EXECUTABLE  0x1000      /* mark it as an executable */
#define MAP_LOCKED  0x2000      /* pages are locked */
#define MAP_NORESERVE   0x4000      /* don't check for reservations */
#define MAP_POPULATE    0x8000      /* populate (prefault) pagetables */
#define MAP_NONBLOCK    0x10000     /* do not block on IO */
#define MAP_STACK   0x20000     /* give out an address that is best suited for process/thread stacks */
#define MAP_HUGETLB 0x40000     /* create a huge page mapping */

下面解释一下上面的各参数:
* MAP_FIXED:使用指定的映射起始地址,如果由start和len参数指定的内存区重叠于现存的映射空间,重叠部分将会被丢弃。如果指定的起始地址不可用,操作将会失败。并且起始地址必须落在页的边界上。
* MAP_SHARED:与其它所有映射这个对象的进程共享映射空间。对共享区的写入,相当于输出到文件。直到msync()或者munmap()被调用,文件实际上不会被更新。
* MAP_PRIVATE:建立一个写入时拷贝的私有映射。内存区域的写入不会影响到原文件。这个标志和MAP_SHARED标志是互斥的,只能使用其中一个。
* MAP_ANONYMOUS:匿名映射,映射区不与任何文件关联。
* MAP_NORESERVE:不要为这个映射保留交换空间。
* MAP_LOCKED:锁定映射区的页面,从而防止页面被交换出内存。
* MAP_POPULATE:为文件映射通过预读的方式准备好页表。随后对映射区的访问不会被页违例阻塞。
* MAP_NONBLOCK:仅和MAP_POPULATE一起使用时才有意义。不执行预读,只为已存在于内存中的页面建立页表入口。
* MAP_STACK:使用更适合于进程或线程栈的地址
* MAP_HUGETLB:巨型页映射

ART中对mmap的封装和应用

ART中封装了MemMap类用于对mmap的封装,并提供了重用的功能。
生成一个MemMap对象,可以通过MapFileAtAddress函数来实现。MapFileAtAddress在mmap的基础上,还增加了reuse参数以支持重用。

其声明如下:

83  // Map part of a file, taking care of non-page aligned offsets.  The
84  // "start" offset is absolute, not relative. This version allows
85  // requesting a specific address for the base of the
86  // mapping. "reuse" allows us to create a view into an existing
87  // mapping where we do not take ownership of the memory.
88  //
89  // On success, returns returns a MemMap instance.  On failure, returns null.
90  static MemMap* MapFileAtAddress(uint8_t* addr, size_t byte_count, int prot, int flags, int fd,
91                                  off_t start, bool reuse, const char* filename,
92                                  std::string* error_msg);
93
``

下面我们来看它的实现:

```cpp
449
450MemMap* MemMap::MapFileAtAddress(uint8_t* expected_ptr, size_t byte_count, int prot, int flags,
451                                 int fd, off_t start, bool reuse, const char* filename,
452                                 std::string* error_msg) {




<div class="se-preview-section-delimiter"></div>

首先,要对上节我们讲过的prot保护属性和flags映射属性二者的有效性进行检查。
prot首先不能是PROT_NONE。
flags得保证MAP_SHARED和MAP_PRIVATE至少有一个成立。

453  CHECK_NE(0, prot);
454  CHECK_NE(0, flags & (MAP_SHARED | MAP_PRIVATE));




<div class="se-preview-section-delimiter"></div>

然后我们开始处理reuse的情况:

456  // Note that we do not allow MAP_FIXED unless reuse == true, i.e we
457  // expect his mapping to be contained within an existing map.
458  if (reuse) {
459    // reuse means it is okay that it overlaps an existing page mapping.
460    // Only use this if you actually made the page reservation yourself.
461    CHECK(expected_ptr != nullptr);
462
463    DCHECK(ContainedWithinExistingMap(expected_ptr, byte_count, error_msg)) << *error_msg;
464    flags |= MAP_FIXED;
465  } else {
466    CHECK_EQ(0, flags & MAP_FIXED);
467    // Don't bother checking for an overlapping region here. We'll
468    // check this if required after the fact inside CheckMapRequest.
469  }




<div class="se-preview-section-delimiter"></div>

如果byte_count是0,就不做mmap了,直接返回一个MemMap对象:

471  if (byte_count == 0) {
472    return new MemMap(filename, nullptr, 0, nullptr, 0, prot, false);
473  }




<div class="se-preview-section-delimiter"></div>

调用mmap之前,我们计算一下页对齐:

474  // Adjust 'offset' to be page-aligned as required by mmap.
475  int page_offset = start % kPageSize;
476  off_t page_aligned_offset = start - page_offset;
477  // Adjust 'byte_count' to be page-aligned as we will map this anyway.
478  size_t page_aligned_byte_count = RoundUp(byte_count + page_offset, kPageSize);
479  // The 'expected_ptr' is modified (if specified, ie non-null) to be page aligned to the file but
480  // not necessarily to virtual memory. mmap will page align 'expected' for us.
481  uint8_t* page_aligned_expected =
482      (expected_ptr == nullptr) ? nullptr : (expected_ptr - page_offset);




<div class="se-preview-section-delimiter"></div>

下面真正调用我们上节讲过的mmap函数:

484  uint8_t* actual = reinterpret_cast<uint8_t*>(mmap(page_aligned_expected,
485                                              page_aligned_byte_count,
486                                              prot,
487                                              flags,
488                                              fd,
489                                              page_aligned_offset));




<div class="se-preview-section-delimiter"></div>

处理map失败的情况:

490  if (actual == MAP_FAILED) {
491    auto saved_errno = errno;
492
493    PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);
494
495    *error_msg = StringPrintf("mmap(%p, %zd, 0x%x, 0x%x, %d, %" PRId64
496                              ") of file '%s' failed: %s. See process maps in the log.",
497                              page_aligned_expected, page_aligned_byte_count, prot, flags, fd,
498                              static_cast<int64_t>(page_aligned_offset), filename,
499                              strerror(saved_errno));
500    return nullptr;
501  }
502  std::ostringstream check_map_request_error_msg;
503  if (!CheckMapRequest(expected_ptr, actual, page_aligned_byte_count, error_msg)) {
504    return nullptr;
505  }




<div class="se-preview-section-delimiter"></div>

成功的话,构造一个MemMap对象并返回。

506  return new MemMap(filename, actual + page_offset, byte_count, actual, page_aligned_byte_count,
507                    prot, reuse);
508}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
在Linux Cmmap的数据传输应用实例可以通过以下代码实现: #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <sys/mman.h> void mmap_transfer(const char* src_file, const char* dest_file) { int src_fd, dest_fd; struct stat src_sb; void* src_start; void* dest_start; src_fd = open(src_file, O_RDONLY); // 打开源文件 dest_fd = open(dest_file, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR); // 创建目标文件 fstat(src_fd, &src_sb); // 获取源文件状态 src_start = mmap(NULL, src_sb.st_size, PROT_READ, MAP_PRIVATE, src_fd, 0); // 建立源文件的内存映射 dest_start = mmap(NULL, src_sb.st_size, PROT_WRITE, MAP_SHARED, dest_fd, 0); // 建立目标文件的内存映射 memcpy(dest_start, src_start, src_sb.st_size); // 将源文件数据复制到目标文件 munmap(src_start, src_sb.st_size); // 解除源文件内存映射 munmap(dest_start, src_sb.st_size); // 解除目标文件内存映射 close(src_fd); // 关闭源文件 close(dest_fd); // 关闭目标文件 } int main() { mmap_transfer("source.txt", "destination.txt"); // 调用mmap传输数据的函数,将source.txt文件内容复制到destination.txt文件 return 0; } 以上代码mmap_transfer函数接受源文件名和目标文件名作为参数,打开源文件和创建目标文件。然后,通过mmap函数建立源文件和目标文件的内存映射。接着,使用memcpy函数将源文件的数据复制到目标文件。最后,使用munmap函数解除内存映射并关闭文件。通过调用mmap_transfer函数,可以实现将一个文件的内容传输到另一个文件。<span class="em">1</span><span class="em">2</span><span class="em">3</span><span class="em">4</span>

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Jtag特工

你的鼓励将是我创作的最大动力

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

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

打赏作者

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

抵扣说明:

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

余额充值