天天看點

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

對于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}

c++

繼續閱讀