Linux에서 사용할 수 없는 것처럼 보이는 메모리 매핑의 목적은 무엇입니까?

Linux에서 사용할 수 없는 것처럼 보이는 메모리 매핑의 목적은 무엇입니까?

각 프로세스에는 읽기, 쓰기 또는 실행이 불가능한 전용 메모리 매핑이 있는 것 같습니다(해당 플래그는 "---p"입니다).

grep -- --- /proc/self/maps
7f2bd9bf7000-7f2bd9df6000 ---p 001be000 fc:00 3733                       /lib/x86_64-linux-gnu/libc-2.19.so
7f2bd9e04000-7f2bda003000 ---p 00003000 fc:00 3743                       /lib/x86_64-linux-gnu/libdl-2.19.so
7f2bda042000-7f2bda241000 ---p 0003d000 fc:00 36067                      /lib/x86_64-linux-gnu/libpcre.so.3.13.1

일부 공유 라이브러리의 콘텐츠를 반환하고 Java(JVM) 프로세스의 경우 수백 메가바이트의 익명 맵 수십 개도 반환합니다.

편집: 이러한 매핑이 자리 표시자인 경우 보호되는 이벤트 및 기타 활동에 대해 누가 이러한 예약된 위치를 사용합니까? 즉, 이러한 자리 표시자가 존재하지 않으면 어떤 오류 동작이 발생할 수 있습니까?

두 번째 편집: 공유 라이브러리의 이러한 구멍은 실제로 어떤 목적으로 컴파일러 및/또는 동적 링커에 도움이 된다는 점을 고려하면 JVM 프로세스에 표시되는 익명 매핑의 이러한 구멍에는 다른 목적이 있어야 합니다. Tomcat JVM 프로세스의 익명 맵을 크기별로 정렬합니다.

 20 MB  00007FA0AAB52000-00007FA0AC000000 ---p 00000000 00:00 0
 41 MB  00007FA0B1603000-00007FA0B4000000 ---p 00000000 00:00 0
 50 MB  00007FA090D04000-00007FA094000000 ---p 00000000 00:00 0
 53 MB  00007FA0F8A40000-00007FA0FC000000 ---p 00000000 00:00 0
 61 MB  00007FA0C42C5000-00007FA0C8000000 ---p 00000000 00:00 0
 61 MB  00007FA0CC29A000-00007FA0D0000000 ---p 00000000 00:00 0
 61 MB  00007FA0D0293000-00007FA0D4000000 ---p 00000000 00:00 0
 62 MB  00007FA0D814C000-00007FA0DC000000 ---p 00000000 00:00 0
 62 MB  00007FA0E017E000-00007FA0E4000000 ---p 00000000 00:00 0
 63 MB  00007FA0B803B000-00007FA0BC000000 ---p 00000000 00:00 0
 63 MB  00007FA0BC021000-00007FA0C0000000 ---p 00000000 00:00 0
 63 MB  00007FA0C0021000-00007FA0C4000000 ---p 00000000 00:00 0
 63 MB  00007FA0D4075000-00007FA0D8000000 ---p 00000000 00:00 0
 63 MB  00007FA0DC040000-00007FA0E0000000 ---p 00000000 00:00 0
 63 MB  00007FA0E4067000-00007FA0E8000000 ---p 00000000 00:00 0
189 MB  00007FA0EC300000-00007FA0F8000000 ---p 00000000 00:00 0
1008 MB  0000000100FF5000-0000000140000000 ---p 00000000 00:00 0

답변1

Null 권한이 있는 2044KB 메모리 영역이 두 개 있습니다. 앞서 언급했듯이 ELF의 "실행 보기"는 실행 가능한 바이너리가 메모리에 로드되는 방식에 중점을 둡니다. ld.so가 동적 라이브러리를 가져올 때 LOAD라고 표시된 세그먼트를 확인합니다(readelf -a xxx.so 명령의 "프로그램 헤더" 및 "섹션 대 세그먼트 매핑" 참조). 일반적으로 두 개의 LOAD 세그먼트가 있고 두 세그먼트 사이에 "구멍"이 있으므로(이 두 세그먼트의 VirtAddr 및 MemSiz 참조) ld.so는 의도적으로 이 구멍에 액세스할 수 없도록 만듭니다. elf/dl-load.c에서 PROT_NONE을 찾습니다. _dl_map_object_from_fd의 기호

http://www.cs.stevens.edu/~jschauma/810/elf.html

mprotectstrace eg 를 사용하여 발생하는 호출로 이 상황을 보는 것도 쉽습니다 strace -f grep -- . /proc/self/maps 2>&1 |less.

open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\300\25\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=471728, ...}) = 0
mmap(NULL, 2564360, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f0e3ad2a000
mprotect(0x7f0e3ad9c000, 2093056, PROT_NONE) = 0
mmap(0x7f0e3af9b000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x71000) = 0x7f0e3af9b000
close(3)                                = 0

github에 glibc repo 미러가 있어서 PROT_NONE 검색은 어렵지 않네요...

/* 이 구현은 (dl-unmap-segments.h의 _dl_unmap_segments의 해당 구현과 같이) 공유 객체가 항상 모든 세그먼트가 연속되도록 배치된다고 가정합니다(또는 세그먼트 사이의 간격이 작아서 바람직하게는 모든 전체 페이지가 보존되는 것이 좋습니다). ) 주소 공간의 해당 부분을 다른 용도로 사용하는 것을 허용하지 않고 PROT_NONE에 의해 매핑된 간격 내에서). */

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/elf/dl-map-segments.h#L21

/* _dl_map_segments는 세그먼트 사이의 간격에 있는 모든 페이지가 PROT_NONE 맵으로 채워지도록 보장합니다. 따라서 우리는 한 번에 전체 범위의 매핑을 해제할 수 있습니다. */

https://github.com/bminor/glibc/blob/73dfd088936b9237599e4ab737c7ae2ea7d710e1/elf/dl-unmap-segments.h#L25

자바

OpenJDK는...PROT_NONE 매핑을 사용하여 커밋되지 않은 주소 공간을 예약합니다(그런 다음 필요에 따라 mprotect 호출을 통해 커밋합니다).

자연스러운 가정은 어떤 이유로 연속적인 힙 메모리를 갖고 싶어한다는 것입니다.

실제로 필요할 때까지 PROT_NONE을 사용하여 공간을 예약합니다. 이 의견의 원래 맥락은 Linux VM 남용에 대한 논의였습니다. 액세스할 수 없는 맵을 사용하면 커널이 다음과 같이 구성된 경우 커널에서 커밋할 필요가 없습니다(맵이 필요하고 액세스 가능해질 때까지).엄격한 커밋 모드.

JVM 컨텍스트에서 이 예약이 왜 미리 필요한지 궁금하다면 링크된 네이티브 코드 사용을 고려해 보세요.JNI또는 mmap을 사용하는 것도 가능할 수 있습니다.

https://lwn.net/Articles/627728/

관련 정보