Qual é o propósito de mapeamentos de memória aparentemente inutilizáveis no linux?

6

Parece que todo processo tem mapeamentos de memória privada que não são legíveis nem graváveis nem executáveis (cujas flags são "--- 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

retorna alguns em bibliotecas compartilhadas e isso faz com que os processos Java (JVM) retornem até mesmo uma dúzia de mapeamentos anônimos com centenas de megabytes.

Editar: se esses mapeamentos forem marcadores de posição, quem usará esses locais mantidos em quais eventos e de quais outras atividades eles estão protegidos - em outras palavras: qual é o comportamento errado que poderia acontecer se esses marcadores de posição não estivessem lá? / p>

2ª edição: dados esses buracos em bibliotecas compartilhadas são de fato úteis para alguma finalidade do compilador e / ou do vinculador dinâmico, deve haver alguma outra finalidade para esses buracos nos mapeamentos anônimos visíveis nos processos da JVM. Ordenou os mapeamentos anônimos de um processo de JVM do tomcat por tamanho:

 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
    
por Juergen 24.03.2017 / 21:11

1 resposta

6

Note that there are two memory regions of 2044KB with null permissions. As mentioned earlier, the ELF's 'execution view' is concerned with how to load an executable binary into memory. When ld.so brings in the dynamic libraries, it looks at the segments labelled as LOAD (look at "Program Headers" and "Section to Segment mapping" from readelf -a xxx.so command.) Usually there are two LOAD segments, and there is a "hole" between the two segments (look at the VirtAddr and MemSiz of these two segments), so ld.so will make this hole inaccessible deliberately: Look for the PROT_NONE symbol in _dl_map_object_from_fd in elf/dl-load.c

link

Também é fácil ver isso acontecendo como uma chamada mprotect , usando strace, por exemplo. strace -f grep -- . /proc/self/maps 2>&1 |less .

open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF
open("/lib64/libpcre.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "7ELF%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0%pre%%pre%%pre%%pre%%pre%%pre%"..., 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
%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%%pre%>%pre%%pre%%pre%%pre%0%pre%%pre%%pre%%pre%%pre%%pre%"..., 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

Existem espelhos do repositório glibc no github, por isso não foi difícil procurar por PROT_NONE ...

/* This implementation assumes (as does the corresponding implementation of _dl_unmap_segments, in dl-unmap-segments.h) that shared objects are always laid out with all segments contiguous (or with gaps between them small enough that it's preferable to reserve all whole pages inside the gaps with PROT_NONE mappings rather than permitting other use of those parts of the address space). */

link

/* _dl_map_segments ensures that any whole pages in gaps between segments are filled in with PROT_NONE mappings. So we can just unmap the whole range in one fell swoop. */

link

Java

OpenJDK ... uses PROT_NONE mappings to reserve uncommitted address space (which is then committed with mprotect calls, as needed).

A suposição natural é que ele deseja ter memória heap contígua por algum motivo.

Ele usa PROT_NONE para reservar espaço, até que ele realmente precise. O contexto original deste comentário é uma discussão sobre o overcommit da VM Linux: o uso de mapeamentos inacessíveis evita qualquer compromisso do kernel (até que o mapeamento seja necessário e disponibilizado), caso o kernel esteja configurado para strict mode de commit .

Se você está se perguntando por que precisa fazer essa reserva antecipadamente no contexto da JVM, considere que o código nativo está vinculado ao uso de JNI ou equivalente também pode estar usando mmap.

link

    
por 24.03.2017 / 22:08