本文共 7023 字,大约阅读时间需要 23 分钟。
对于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 LEN76 bytes. Returns 0 if successful, -1 for errors (and sets errno). */extern int munmap (void *__addr, size_t __len);
地址,长度,偏移量,文件ID都容易理解,复杂的参数只有两个,prot和flags这两项。
prot:内存保护标志,是否允许读,写,执行,不能与文件的打开模式冲突。
可取的值如下:以上4个在mmap的man page中有说明。还有一个虽然man中没有说明,但是也可以使用:
这几个宏定义于/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 */
下面解释一下上面的各参数:
ART中封装了MemMap类用于对mmap的封装,并提供了重用的功能。
生成一个MemMap对象,可以通过MapFileAtAddress函数来实现。MapFileAtAddress在mmap的基础上,还增加了reuse参数以支持重用。其声明如下:
83 // Map part of a file, taking care of non-page aligned offsets. The84 // "start" offset is absolute, not relative. This version allows85 // requesting a specific address for the base of the86 // mapping. "reuse" allows us to create a view into an existing87 // 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);462463 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'll468 // 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 but480 // 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;492493 PrintFileToLog("/proc/self/maps", LogSeverity::WARNING);494495 *error_msg = StringPrintf("mmap(%p, %zd, 0x%x, 0x%x, %d, %" PRId64496 ") 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}转载地址:http://mbdux.baihongyu.com/