2013-01-17 7 views
8

Moje podstawowe pytanie brzmi: dlaczego VSIZE dla procesu 64-bitowego jest o wiele większy niż ten sam program skompilowany na 32-bitowy?Dlaczego VSIZE jest o wiele większy w 64-bitowym procesie Linux?

Poniżej przedstawiono dane wyjściowe pliku/proc/<pid>/maps dla procesu 32-bitowego.

00148000-00149000 r-xp 00000000 00:00 0    [vdso] 
00149000-002d2000 r-xp 00000000 fd:02 8914142   /lib/libc-2.12.so 
002d2000-002d3000 ---p 00189000 fd:02 8914142   /lib/libc-2.12.so 
002d3000-002d5000 r--p 00189000 fd:02 8914142   /lib/libc-2.12.so 
002d5000-002d6000 rw-p 0018b000 fd:02 8914142   /lib/libc-2.12.so 
002d6000-002d9000 rw-p 00000000 00:00 0 
005c9000-005da000 r-xp 00000000 fd:02 17059392  /tmp/vsizetest/lib/libtesting.so 
005da000-005db000 rw-p 00010000 fd:02 17059392  /tmp/vsizetest/lib/libtesting.so 
005db000-0061b000 rw-p 00000000 00:00 0 
00661000-00689000 r-xp 00000000 fd:02 8917713   /lib/libm-2.12.so 
00689000-0068a000 r--p 00027000 fd:02 8917713   /lib/libm-2.12.so 
0068a000-0068b000 rw-p 00028000 fd:02 8917713   /lib/libm-2.12.so 
00694000-006ab000 r-xp 00000000 fd:02 8917680   /lib/libpthread-2.12.so 
006ab000-006ac000 r--p 00016000 fd:02 8917680   /lib/libpthread-2.12.so 
006ac000-006ad000 rw-p 00017000 fd:02 8917680   /lib/libpthread-2.12.so 
006ad000-006af000 rw-p 00000000 00:00 0 
006e5000-00703000 r-xp 00000000 fd:00 3150403   /lib/ld-2.12.so 
00703000-00704000 r--p 0001d000 fd:00 3150403   /lib/ld-2.12.so 
00704000-00705000 rw-p 0001e000 fd:00 3150403   /lib/ld-2.12.so 
00983000-009a0000 r-xp 00000000 fd:02 8914997   /lib/libgcc_s-4.4.5-20110214.so.1 
009a0000-009a1000 rw-p 0001d000 fd:02 8914997   /lib/libgcc_s-4.4.5-20110214.so.1 
00ca5000-00d86000 r-xp 00000000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d86000-00d8a000 r--p 000e0000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d8a000-00d8c000 rw-p 000e4000 fd:02 6300601   /usr/lib/libstdc++.so.6.0.13 
00d8c000-00d92000 rw-p 00000000 00:00 0 
08048000-08049000 r-xp 00000000 fd:02 21134666  /tmp/vsizetest/bin/testvsz 
08049000-0804a000 rw-p 00000000 fd:02 21134666  /tmp/vsizetest/bin/testvsz 
09b8d000-09bae000 rw-p 00000000 00:00 0    [heap] 
f7796000-f779c000 rw-p 00000000 00:00 0 
ff998000-ff9ae000 rw-p 00000000 00:00 0    [stack] 

co skutkuje całkowitym vsize z 3656.

Poniżej jest wyjście < pid pliku/proc/>/Mapy dla procesu 64 bitów.

00400000-00401000 r-xp 00000000 fd:02 21134667    /tmp/vsizetest/bin64/testvsz 
00600000-00601000 rw-p 00000000 fd:02 21134667    /tmp/vsizetest/bin64/testvsz 
02301000-02322000 rw-p 00000000 00:00 0      [heap] 
3b7c800000-3b7c820000 r-xp 00000000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca1f000-3b7ca20000 r--p 0001f000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca20000-3b7ca21000 rw-p 00020000 fd:00 661349   /lib64/ld-2.12.so 
3b7ca21000-3b7ca22000 rw-p 00000000 00:00 0 
3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350   /lib64/libc-2.12.so 
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8b000-3b7cf90000 rw-p 00000000 00:00 0 
3b7d000000-3b7d083000 r-xp 00000000 fd:00 661365   /lib64/libm-2.12.so 
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365   /lib64/libm-2.12.so 
3b7d282000-3b7d283000 r--p 00082000 fd:00 661365   /lib64/libm-2.12.so 
3b7d283000-3b7d284000 rw-p 00083000 fd:00 661365   /lib64/libm-2.12.so 
3b7d800000-3b7d817000 r-xp 00000000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da16000-3b7da17000 r--p 00016000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da17000-3b7da18000 rw-p 00017000 fd:00 661352   /lib64/libpthread-2.12.so 
3b7da18000-3b7da1c000 rw-p 00000000 00:00 0 
3b7e000000-3b7e007000 r-xp 00000000 fd:00 661361   /lib64/librt-2.12.so 
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361   /lib64/librt-2.12.so 
3b7e206000-3b7e207000 r--p 00006000 fd:00 661361   /lib64/librt-2.12.so 
3b7e207000-3b7e208000 rw-p 00007000 fd:00 661361   /lib64/librt-2.12.so 
3b87000000-3b87016000 r-xp 00000000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3b87016000-3b87215000 ---p 00016000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3b87215000-3b87216000 rw-p 00015000 fd:00 664219   /lib64/libgcc_s-4.4.6-20110824.so.1 
3d44c00000-3d44ce8000 r-xp 00000000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ee8000-3d44eef000 r--p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44eef000-3d44ef1000 rw-p 000ef000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
3d44ef1000-3d44f06000 rw-p 00000000 00:00 0 
7f30ab397000-7f30ab39c000 rw-p 00000000 00:00 0 
7f30ab39c000-7f30ab3ad000 r-xp 00000000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab5ac000-7f30ab5ad000 rw-p 00010000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 
7f30ab5ad000-7f30ab5ee000 rw-p 00000000 00:00 0 
7f30ab606000-7f30ab609000 rw-p 00000000 00:00 0 
7fff69512000-7fff69528000 rw-p 00000000 00:00 0    [stack] 
7fff695ff000-7fff69600000 r-xp 00000000 00:00 0    [vdso] 
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0  [vsyscall] 

co skutkuje vsize z 18480.

Główną różnicą pomiędzy 2 mapami są następujące wpisy z danych 64 bit:

3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350    /lib64/libc-2.12.so 
3b7d083000-3b7d282000 ---p 00083000 fd:00 661365    /lib64/libm-2.12.so 
3b7d817000-3b7da16000 ---p 00017000 fd:00 661352    /lib64/libpthread-2.12.so 
3b7e007000-3b7e206000 ---p 00007000 fd:00 661361    /lib64/librt-2.12.so 
3b87016000-3b87215000 ---p 00016000 fd:00 664219    /lib64/libgcc_s-4.4.6-20110824.so.1 
3d44ce8000-3d44ee8000 ---p 000e8000 fd:00 3019214   /usr/lib64/libstdc++.so.6.0.13 
7f30ab3ad000-7f30ab5ac000 ---p 00011000 fd:02 21127804  /tmp/vsizetest/lib64/libtesting.so 

stanowiących 14316 z 18480 VSIZE.

Inne eksperymenty z innymi programami wydają się pokazywać, że w 64-bitowym wydaniu wydaje się, że otrzymujesz jedną z tych prywatnych, nieczytelnych, niepisywalnych, niewykonywalnych porcji pamięci dla każdej współużytkowanej biblioteki, która jest używana przez proces, podczas gdy w wersji 32-bitowej nie ma prawie żadnej z tych części.

Czy ktoś wie, co to są te fragmenty pamięci?

Uwaga: na podstawie niektórych odpowiedzi na podobne pytanie, What these memory regions for, from a Linux process?, nie jest to proces wielowątkowy i jest już skompilowany -fPIC.

Odpowiedz

1

[Nie naprawdę odpowiedzią ... mówienie obok mojej wiedzy]

Jeżeli segmenty są naprawdę „prywatny, non-czytelny, nie zapisywalny, non-wykonywalny” potem nigdy nie powinny być określane, i chociaż istnieją one w przestrzeni pamięci WIRTUALNEJ, nigdy nie zajmują żadnej prawdziwej pamięci, a zatem nie ma się czym martwić. (?)

To musi być jakaś kwestia księgowości lub fragmentacji. Ponieważ są one częścią bibliotek współdzielonych (* .so), to właśnie w ten sposób zbudowano te biblioteki. To naprawdę nie ma nic wspólnego z twoim programem, poza tym, że jest powiązany z tymi bibliotekami. Chyba że chcesz przebudować te biblioteki, lub nie używać ich, nie ma wiele do zrobienia na ten temat (i nie wiele zyskać, ponieważ i tak nie powinny używać żadnej prawdziwej pamięci).

Może powiązane? W What these memory regions for, from a Linux process?

@ cf mówi, że niektóre segmenty pamięci, które są "--- p", to "strony strażnicze".

To sugeruje, że istnieją tylko po to, aby złapać zbłąkany wskaźnik lub stos, który rośnie do bardzo bladego ... rodzaju twardego separatora w pamięci, aby system mógł złapać typowy błąd i przerwać przetwarzanie, zamiast pozwolić, by te zwykłe błędy wymknęły się (jest to fatalny błąd, aby się do nich odnosić i naprawdę NIGDY nie użyje żadnej prawdziwej pamięci).

1

Odpowiedź na pytanie, dlaczego i co stanowi bibliotekę współdzieloną 64-bitową ma dodatkową porcję pamięci, polega na pobraniu przykładowego ładowania libc.so i sprawdzeniu, w jaki sposób ładunek ładuje biblioteki dynamiczne.Poniżej znajdują się strace wyjścia dla plików wykonywalnych 32-bitowych i 64-bitowych, które mówią nam, że są połączenia z mmap & mprotect.

[email protected]:~/32_64bit$ strace ./crash-x86-64 
... 
open("/lib/x86_64-linux-gnu/libc.so.6", 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\200\30\2\0\0\0\0\0"..., 
832) = 832 
fstat(3, {st_mode=S_IFREG|0755, st_size=1811128, ...}) = 0 
mmap(NULL, 3925208, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) =  
0x7fa354f8a000 
mprotect(0x7fa35513f000, 2093056, PROT_NONE) = 0 
mmap(0x7fa35533e000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 
3, 0x1b4000) = 0x7fa35533e000 
mmap(0x7fa355344000, 17624, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 
-1, 0) = 0x7fa355344000 
close(3)        = 0 
... 
[email protected]:~/32_64bit$ strace ./crash 
... 
open("/lib/i386-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3 
read(3, "\177ELF\1\1\1\0\0\0\0\0\0\0\0\0\3\0\3\0\1\0\0\0000\226\1\0004\0\0\0"..., 
512) = 512 
fstat64(3, {st_mode=S_IFREG|0755, st_size=1730024, ...}) = 0 
mmap2(NULL, 1743580, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 
0xfffffffff7546000 
mprotect(0xf76e9000, 4096, PROT_NONE) = 0 
mmap2(0xf76ea000, 12288, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 
3, 0x1a3) = 0xfffffffff76ea000 
mmap2(0xf76ed000, 10972, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, 
-1, 0) = 0xfffffffff76ed000 
close(3)        = 0 
... 

ściśle przestrzegając zarówno strace za dwie rzeczy muszą być zbadanie,

1. Każdy z nich odwzorowuje pamięci 3 razy i 1 wezwanie do mprotect dokładnie po pierwszym mmap .

2. Porównując mprotect zaproszenia do 64bit & 32bit ma 2093056B & 4096B regionu odpowiednio zabezpieczonym.

W dl-load.c, podprogram _dl_map_object_from_fd() odwzorowuje segmenty pamięci dynamiczne biblioteki wirtualnej przestrzeni przez ustawienie wymaganych uprawnień i zero wypełnia .bss sekcji biblioteki i aktualizuje strukturę link Mapa. Pozwala uzyskać tutaj jakąś część kodu dla dalszych analiz,

struct link_map * 
_dl_map_object_from_fd () 
{ 
... 
    /* Scan the program header table, collecting its load commands. */ 
    struct loadcmd 
    { 
    ElfW(Addr) mapstart, mapend, dataend, allocend; 
    off_t mapoff; 
    int prot; 
    } loadcmds[l->l_phnum], *c; // l is link_map struct described for each object 
            of dynamic linker 
    size_t nloadcmds = 0; 
    bool has_holes = false; 
    ... 
    for (ph = phdr; ph < &phdr[l->l_phnum]; ++ph) 
    switch (ph->p_type) 
    { 
    ... 
    case PT_LOAD: 
    ... 
    c = &loadcmds[nloadcmds++]; 
    c->mapstart = ph->p_vaddr & ~(GLRO(dl_pagesize) - 1); 
    c->mapend = ((ph->p_vaddr + ph->p_filesz + GLRO(dl_pagesize) - 1) 
        & ~(GLRO(dl_pagesize) - 1)); 
    ... 
    if (nloadcmds > 1 && c[-1].mapend != c->mapstart) 
     has_holes = true; 
    ... 
    } 
    ... 
    if (has_holes) 
     __mprotect ((caddr_t) (l->l_addr + c->mapend), 
      loadcmds[nloadcmds - 1].mapstart - c->mapend, PROT_NONE); 
    ... 
} 

W powyższym kodzie l_phnum stosowanych w for rachunku posiada liczbę wpisów w nagłówku programu ELF. Idealnie dla każdej iteracji każdy segment wejściowy jest odwzorowany. Kiedy PT_LOAD przypadek segmentu trafia do jej pierwszy raz, jej zasadzie sekcji .text lub .rodata który dostaje mmapped (1st mmap w strace) i drugi PT_LOAD segmentu reprezentuje .data sekcja zostaje odwzorowany (2nd mmap w strace). Przed zamapowaniem drugiego segmentu mapstart i mapend, które odnoszą się do początku i końca sekcji tekstu. W następnej PT_LOAD iteracji, jeśli poprzedni segment mapend nie jest równy bieżącemu (.data) segmentowi mapstart, to jest ich otwór między dwoma segmentami PT_LOAD (co oznacza lukę między sekcjami .text i .data). Dlatego, jeśli jest to luka między regionami pamięci z zerowymi uprawnieniami, program ładujący będzie chronił (mprotect wywołanie w strace) lub uniemożliwi dostęp. Obszar chroniony dla procesu 64-bitowego i 32-bitowego to: Vs tylko strona odpowiednio dodająca do ogromnego fragmentu pamięci dla 64-bitowych bibliotek.

Dowód na 64bit regionu niedostępnej: objdump dla libc.so poniżej daje nam wirtualny adres (VA) statystyki, które są zaokrąglenia odpowiednio w następujący sposób

    PT_LOAD(1)    PT_LOAD(2)      
mapstart VA 0x0000000000000000  0x00000000003b4000 
mapend VA 0x00000000001b5000  0x00000000003A0000 

Tutaj PT_LOAD(1) mapend (0x00000000001b5000) nie jest równa PT_LOAD(2) mapstart (0x00000000003b4000), co powoduje powstanie otworu pamięci o wartości 0x00000000001FF000 (w systemie dziesiętnym 2093056B).

[email protected]:~/32_64bit$objdump -x -s -d -D /lib/x86_64-linux-gnu/libc.so.6 
Program Header: 
... 
    LOAD off 0x0000000000000000 vaddr 0x0000000000000000 paddr 0x0000000000000000 align 2**21 
     filesz 0x00000000001b411c memsz 0x00000000001b411c flags r-x 
    LOAD off 0x00000000001b4700 vaddr 0x00000000003b4700 paddr 0x00000000003b4700 align 2**21 
     filesz 0x0000000000005160 memsz 0x0000000000009dd8 flags rw- 
... 

Na górę 64-bitowego tekstu zajmuje wyższe reprezentacja nauczania bajtów porównaniu do 32bit. Podobnie rozmiar wskaźników na 64-bitach to 8B, dodając kolejne bajty o wartości 4.Również wyrównanie struktury danych jest ustawione na 64-bitowe, co powoduje, że zmapowane obszary są większe.

Proste size komendy na plikach binarnych może pokazać różnicę między regionami 32/64 bitowych pamięci programów jak poniżej,

[email protected]:~/32_64bit$ ls -lrt 
total 10368 
-rwxrwxrwx 1 esunboj ei 5758776 Oct 10 11:35 crash-x86-64 
-rwxrwxrwx 1 esunboj ei 4855676 Oct 10 11:36 crash 
[email protected]:~/32_64bit$ size crash 
    text data  bss  dec  hex filename 
4771286 82468 308704 5162458 4ec5da crash 
[email protected]:~/32_64bit$ size crash-x86-64 
    text data  bss  dec  hex filename 
5634861 121164 1623728 7379753 709b29 crash-x86-64 
8

Główną różnicą vsize pochodzi od sposobu mapowania PROT_NONE (tryb „P”) --- współdzielonych bibliotek jest wykonywana w przypadku wersji 32-bitowej i 64-bitowej.

To są dokładnie odwzorowania, które zostały zauważone jako tworzące różnicę.

Generalnie dla każdej udostępnionej biblioteki ładowane będziemy mieli cztery mapowania:

3b7cc00000-3b7cd86000 r-xp 00000000 fd:00 661350   /lib64/libc-2.12.so 
3b7cd86000-3b7cf86000 ---p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf86000-3b7cf8a000 r--p 00186000 fd:00 661350   /lib64/libc-2.12.so 
3b7cf8a000-3b7cf8b000 rw-p 0018a000 fd:00 661350   /lib64/libc-2.12.so 

Pierwszym z nich jest segment kodu z uprawnieniami wykonywalnego sekundzie PROT_NONE (tryb ---) mapping (strony nie może dostęp do nich), a dwa ostatnie segment danych (tylko do odczytu i zapisu do odczytu).

Rozmiar PROT_NONE ma rozmiar MAXPAGESIZE i dlatego jest tworzony inaczej w wersjach 32-bitowej i 64-bitowej. W przypadku wersji 32-bitowej ma rozmiar 4KB (MAXPAGESIZE dla i386), a w przypadku wersji 64-bitowej 2MB (standard MAXPAGESIZE dla systemów x86_64).

Należy zauważyć, że pamięć ta nie jest faktycznie zużyte (to po prostu zużywa adresy przestrzeni adresowej), jak wspomniano tutaj:

http://www.greenend.org.uk/rjk/tech/dataseg.html

„Ta dodatkowa nie kosztuje żadnego RAM lub zamianę przestrzeń, po prostu przestrzeń adresowa w każdym procesie, która jest obfita podaż na platformach 64-bitowych. Podstawową przyczyną jest zapewnienie wydajnego udostępniania bibliotek, ale implementacja jest trochę dziwna. "

Tylko ostatnia sztuczka, znajdę łatwiej sprawdzić mapowania pamięci za pomocą programu narzędziowego pmap niż analizuje plik mapy i produkuje wyjście prostsze w brzmieniu:

Dla skrócie:

pmap <PID> 

przez dłuższy info:

pmap -x <PID> 
+0

fajna odpowiedź, wskazana pamięć jest ładowana przez stronę. Po prostu dodaj, że pmap jest implementowany przez czytanie "/ proc//maps" ... :-) – tristan

+0

Po prostu zmodyfikowałam zdanie, aby wyjaśnić, jak działa pmap. Najlepszym punktem na temat pmap jest to, że analizuje on plik mapy i oblicza dla ciebie rozmiar regionów. – alcachi

Powiązane problemy