對于mmap,大家應該都很熟悉,是一個将檔案映象到記憶體裡,從此以後就可以直接讀寫記憶體,不用再通過檔案通路的方式再去做流式操作的系統調用. 這樣,也可以讓檔案映射的記憶體被多個程序共享,不用每個檔案都共享一份。
mmap調用定義于/usr/include/sys/mman.h中,原型如下:
我們看下帶詳細說明的版本:
位址,長度,偏移量,檔案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中:
需要注意的是,映射類型的标志,有位于mman-common.h中的,也有位于mman.h中的,移植的時候請注意相容性。
下面這幾個通用的,定義于/usr/include/asm-generic/mman-common.h中:
還有一些擴充的參數,定義于/usr/include/asm-generic/mman.h中:
下面解釋一下上面的各參數:
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中封裝了memmap類用于對mmap的封裝,并提供了重用的功能。
生成一個memmap對象,可以通過mapfileataddress函數來實作。mapfileataddress在mmap的基礎上,還增加了reuse參數以支援重用。
其聲明如下:
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) {
453 check_ne(0, prot);
454 check_ne(0, flags & (map_shared | map_private));
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 }
471 if (byte_count == 0) {
472 return new memmap(filename, nullptr, 0, nullptr, 0, prot, false);
473 }
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);
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));
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 }
506 return new memmap(filename, actual + page_offset, byte_count, actual, page_aligned_byte_count,
507 prot, reuse);
508}