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

简介: 我们通过mmap为入门途径,开始探索ART的内存分配吧〜

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
``

下面我们来看它的实现:

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) {


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

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


然后我们开始处理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 }


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

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


调用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);


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

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


处理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(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 }


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

506 return new MemMap(filename, actual + page_offset, byte_count, actual, page_aligned_byte_count,
507 prot, reuse);
508}

目录
相关文章
|
安全 调度 开发者
内核笔记](七)——内核对象(Kernel object)机制
内核笔记](七)——内核对象(Kernel object)机制
466 0
内核笔记](七)——内核对象(Kernel object)机制
|
9月前
|
存储 缓存 BI
|
10月前
|
Java 编译器 Linux
Dalvik和ART的区别
Dalvik和ART的区别
112 0
|
Java BI Android开发
MAT(Memory Analyer Tool)入门拾遗
MAT是基于Eclipse的内存分析工具,MAT(Memory Analyer Tool),它是一个快速、功能丰富的Java Heap的分析工具,它可以帮助我们查找内存泄漏和排查问题,而且还有丰富的报表统计等等。我们可以通过插件的形式运行还可以单独下载,在前文中我们介绍了如何安装MAT工具,详情可以翻阅查看。
118 1
MAT(Memory Analyer Tool)入门拾遗
|
Linux Python 消息中间件