8

私の基本的な質問は、64 ビット プロセスの VSIZE が、32 ビット用にコンパイルされたまったく同じプログラムの VSIZE よりもはるかに大きいのはなぜですか?

以下は、32 ビット プロセスの /proc/<pid>/maps ファイルの出力です。

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]

その結果、合計 VSIZE は 3656 になります。

以下は、64 ビット プロセスの /proc/<pid>/maps ファイルの出力です。

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]

その結果、VSIZE は 18480 になります。

2 つのマップの主な違いは、64 ビット データからの次のエントリです。

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

18480 VSIZE の 14316 を占めます。

他のプログラムでの他の実験では、64ビットでは、プロセスによって使用される共有ライブラリごとに、これらのプライベートで読み取り不可、書き込み不可、実行不可のメモリチャンクの1つを取得するように見えることが示されているようですが、32ビットではこれらのチャンクはほとんどありません。

これらのメモリのチャンクが何であるかを知っている人はいますか?

注: 同様の質問へのいくつかの回答に基づいて、Linux プロセスからのこれらのメモリ領域は何のために使用されますか? 、これはマルチスレッド プロセスではなく、既にコンパイル済みの -fPIC です。

4

3 に答える 3

8

VSIZE の主な違いは、32 ビット バージョンと 64 ビット バージョンの場合に、共有ライブラリの PROT_NONE マッピング (モード "---p") がどのように行われるかによるものです。

これらは、違いを生み出すものとして見つけたマッピングです。

一般に、ロードされた共有ライブラリごとに 4 つのマッピングがあります。

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

最初の 1 つは実行権限を持つコード セグメントで、2 番目は PROT_NONE (モード ---) マッピング (ページにアクセスできない可能性があります)、最後の 2 つはデータ セグメント (読み取り専用部分と読み書き) です。

PROT_NONE のサイズは MAXPAGESIZE であるため、32 ビット バージョンと 64 ビット バージョンでは異なる方法で作成されます。32 ビット バージョンの場合、サイズは 4KB (i386 の MAXPAGESIZE) で、64 ビット バージョンの場合は 2MB (x86_64 システムの標準 MAXPAGESIZE) です。

ここに記載されているように、このメモリは実際には消費されないことに注意してください (アドレス空間のアドレスを消費するだけです)。

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

「この追加は、RAM やスワップ スペースを必要とせず、64 ビット プラットフォームでは十分に供給されている各プロセス内のアドレス スペースだけです。根本的な理由は、ライブラリを効率的に共有可能に保つことですが、実装は少し複雑です。奇数。"

最後のトリックとして、マップ ファイルを解析して読みやすい出力を生成するよりも、 pmap ユーティリティを使用してメモリ マッピングを確認する方が簡単であることがわかりました。

基本情報:

pmap <PID>

詳細情報:

pmap -x <PID>
于 2013-10-10T16:33:42.520 に答える
1

[実際には答えではありません...私の知識を超えて話します]

メモリセグメントが本当に「プライベート、読み取り不可、書き込み不可、実行不可」である場合、それらは決して参照されるべきではなく、それらが仮想メモリ空​​間に存在していても、実際のメモリを占有することはないため、あまり心配する必要はありません。(?)

それはある種の帳簿管理または断片化の問題であるに違いありません。これらは共有ライブラリ (*.so) の一部であるため、これらのライブラリがどのように構築されたかにすぎません。それらのライブラリにリンクされていることを除けば、実際にはプログラムとは何の関係もありません。これらのライブラリを再構築したくない場合、または使用しない場合を除いて、それについて行うことはあまりありません (とにかく実メモリを使用しないため、得るものはあまりありません)。

もしかして関連?Linuxプロセスから、これらのメモリ領域は何のためにありますか?

@caf は、「---p」である一部のメモリセグメントは「ガードページ」であると言います。

それは、それらが迷子のポインターまたはスタックがはるかにエラーに成長するのをキャッチするためだけに存在することを示唆しています...メモリ内の一種のハードセパレーターであるため、システムは一般的なエラーをキャッチし、それらの一般的なエラーをすり抜けるのではなく処理を停止できます(致命的なエラーです)それらをまったく参照することはできず、実際には実際のメモリを使用することはありません)。

于 2013-03-24T13:50:28.603 に答える
1

64ビット共有ライブラリを構成する理由と構成要素に追加のメモリチャンクがあることへの答えは、ロードの例を取り上げ、libc.soローダーが動的ライブラリをロードする方法からこれを調べることです。以下は、 &straceへの呼び出しがあることを示す 32 ビットと 64 ビットの両方の実行可能ファイルの出力です。mmapmprotect

esunboj@L9AGC12:~/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
...
esunboj@L9AGC12:~/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
...

strace の 2 つのことの両方を注意深く観察して調査する必要があります。

1. それぞれがメモリを 3 回マップし、最初の呼び出しmprotectの直後に1 回呼び出しますmmap

mprotect2. 64 ビットと 32 ビットの呼び出しを 比較すると、それぞれ2093056B&4096Bの領域が保護されています。

dl-load.cでは、サブルーチン_dl_map_object_from_fd () は、必要なパーミッションを設定し、ライブラリのゼロ フィル セクションを設定して、動的ライブラリ メモリ セグメントを仮想空間に.bssマップし、リンク マップ構造を更新します。詳細な分析のためにコードの一部をここに取得しましょう。

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);
  ...
}

ステートメントl_phnumで使用される上記のコードでは、ELF プログラム ヘッダーのエントリ数を保持します。for理想的には、反復ごとに各エントリ セグメントがマップされます。PT_LOADセグメントケースが初めてヒットすると、基本的にはまたは.textセクション.rodataが mmap され ( mmapstrace の 1 番目)、2 番目PT_LOADのセグメントがセクションを表す.dataセクションがマップされます ( mmapstrace の 2 番目)。PT_LOAD2 番目のセグメントがマップさmapstartれる前mapendに、テキスト セクションの開始と終了を参照する保持されます。次のPT_LOAD反復で、前のセグメントmapendが現在の (.data) セグメントと等しくない場合mapstart、それらは 2 つのセグメント間の穴です (とPT_LOADの間のギャップを意味します)。.text.dataセクション)。したがって、アクセス権が null のメモリ領域間に穴がある場合、ローダーはそれを保護する ( mprotectstrace を呼び出す) か、アクセスできないようにします。64 ビットおよび 32 ビット プロセスの保護領域は511対64 ビット ライブラリの巨大なメモリ チャンクにそれぞれ1ページ追加されます。

64 ビットのアクセスできない領域の証明: 以下の Objdump はlibc.so、次のように適切に丸められた仮想アドレス (VA) 統計を提供します。

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

ここでPT_LOAD(1) mapend(0x00000000001b5000) は (0x00000000003b4000) と等しくないためPT_LOAD(2) mapstart、0x00000000001FF000 (10 進数2093056B) のメモリ ホールが発生します。

esunboj@L9AGC12:~/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- 
...

最上位の 64 ビット テキストは、32 ビットに比べて命令バイトの表現が多くなります。同様に、64 ビットのポインターのサイズは、さらにバイトを8B追加しています。4また、データ構造のアラインメントは8B64 ビットでアラインされ、マップされた領域が大きくなります。

バイナリの簡単なsizeコマンドは、以下のように 32/64 ビット プログラムのメモリ領域の違いを表示できます。

esunboj@L9AGC12:~/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
esunboj@L9AGC12:~/32_64bit$ size crash
   text    data     bss     dec     hex filename
4771286   82468  308704 5162458  4ec5da crash
esunboj@L9AGC12:~/32_64bit$ size crash-x86-64 
   text    data     bss     dec     hex filename
5634861  121164 1623728 7379753  709b29 crash-x86-64
于 2013-10-10T13:21:39.860 に答える