2

C、C++、または Fortran コードで動作するライブラリを C で開発しています。使用するメカニズムの 1 つは、スタック、ヒープ、またはデータ/bss セグメント内のページへの書き込みをトラップすることです。この場合の「ヒープ」は、マップされたファイルからライブラリが作成する特別なヒープです。ライブラリが Fortran アプリケーションの変数への書き込みをトラップできないことがわかりました。変数は次のように宣言されます

double precision u(5,I,J,K)

ここで、I、J、および K は整数パラメーター (定数) です。次に、コードは「フィールド」と呼ばれる共通ブロックに u を含めます。

GDB の下でデバッグすると、u のアドレスが 3 つのデータ セグメントのいずれの範囲にも収まらないことがわかりました。(そのため、ライブラリは書き込みをトラップできませんでした!) 次に、/proc//maps 疑似ファイルを調べたところ、u のアドレスが、システムが「ヒープ」として注釈を付ける範囲内にあることがわかりました。しかし、どうやってこの「ヒープ」に入ったのですか?この場合の Fortran 77 コードは、非標準の「割り当て」キーワードを使用してヒープに割り当てません。Fortran 77 (Ubuntu Linux x86-64 の下) が「ヒープ」に割り当てる変数と、この「ヒープ」が最初にどのように作成されるかを誰かに説明してもらえますか?

4

1 に答える 1

7

共通ブロックの配列をいじってみました。Linux の .bss セグメントは、実際にはヒープとマージされているように見えます (または、少なくとも同じbrk(2)メカニズムを使用してスペースが割り当てられています)。

関連する Fortran コードは次のとおりです。

double precision u(5,20,20,20)
common /a/ u

gfortran生成される GNU アセンブリ ディレクティブは次のとおりです。

.comm a_,320000,32

a_これは、320000 バイトの大きさで、32 バイト境界に配置する必要があるという名前の共通シンボルを宣言します。リンカーがこの宣言を認識し、他の定義がないa_場合、.bss にスペースが予約されます。これはobjdump、結果のバイナリで実行することから明確にわかります。

 Sections:
 Idx Name          Size      VMA               LMA               File off  Algn
 ...
  22 .data         00000010  0000000000600b40  0000000000600b40  00000b40  2**3
                   CONTENTS, ALLOC, LOAD, DATA
  23 .bss          0004e220  0000000000600b60  0000000000600b60  00000b50  2**5
                   ALLOC
 ...

ここで、.bss は 320000 (0x4e200) バイトと 32 バイトの追加データです。割り当て可能としてマークされるだけで、それ以上のことはありません。ファイルからデータが事前入力されることはありません。また、VMA 0x600b80 で始まるa_ため、32 バイトの追加データが前に置かれていると推測できます。u

(gdb) info address u
Symbol "u" is static storage at address 0x600b80.
(gdb) info symbol &u
a_ in section .bss of /path/to/a.out

uは実際には Fortran のメイン関数のローカル変数のシンボルにすぎませんa_が、 はグローバルに可視なストレージです。そのため、異なるサブルーチン/関数で配列に異なる名前を付けることができますが、それらを適切な共通ブロックに配置すると、同じメモリにアクセスできます。

.bss の厄介な VMA は、.bss がメモリ内の .data セクションの直後に配置されているため、ELF ファイル内の .data セグメントのオフセットの結果であるように見えます。.data セグメントが Linux にロードされる方法は、マッピングにコピー オン ライト セマンティクスを与えるmmap(2)ファイルから -ed されることです。MAP_PRIVATE

00400000-00401000 r-xp 00000000 00:1d 25681168    /path/to/a.out
00600000-00601000 rw-p 00000000 00:1d 25681168    /path/to/a.out <-- .data
00601000-00670000 rw-p 00000000 00:00 0           [heap]

.bss は .data マッピングと同じページで始まります。これは、両方が読み取り/書き込みデータを保持し、書き込まれることが予想され、別のページで .bss を開始しないことで VM のビットが節約されるため、理にかなっています。

.data セグメントの後のすべては、ファイル マッピングによってバックアップされないため、 のように表示される動的に調整可能なスペースに収まり[heap]ます/proc/pid/maps。このスペースとヒープ自体は、データ セグメントの末尾を移動することによって制御されます。これはいわゆるプログラム ブレークと呼ばれbrk(2)ます。straceカーネルの ELF ローダーは、実行可能ファイルで確認できるように、最初にプログラム ブレークを .bss 用のスペースを確保するのに十分な距離まで移動します。

execve("./a.out", ["a.out"], [/* 230 vars */]) = 0
brk(0)                          = 0x64f000 <-- already moved past the .bss

.data セグメントが 00600000 で始まることがわかっています。.bss は 00600B60 で始まります。共通ブロックは 0x600b80 に割り当てられ、そのサイズは 0x4e200 であるため、0x64ed80 で終了し、ページ境界に切り上げられて 0x64f000 になります。ここで、他の動的にリンクされたライブラリが独自にスペースを割り当てない場合、実際のプログラム ヒープが開始されます。

動的メモリ アロケータmalloc(3)は同じbrk(2)メカニズム (mmap(2)大規模な割り当ての場合、またはデータ セグメント サイズの制限に達した場合は匿名) を使用するため、配列が .bss にあったか、.bss で割り当てられたかは問題ではありませんALLOCATE()。違いは、.bss が最初にゼロで埋められ、mallocまたはによって割り当てられたメモリの内容ALLOCATE()がそのまま残されることです。

于 2012-05-07T12:03:13.580 に答える