Linux で共有ライブラリ (ファイル) を使用するアプリケーションがあるとします.so
。質問は、それらのライブラリのコードはメイン アプリケーションと同じヒープにメモリを割り当てるのか、それとも独自のヒープを使用するのかということです。
たとえば、.so
ファイル内の一部の関数が を呼び出す場合malloc
、アプリケーションと同じヒープ マネージャーを使用するのでしょうか、それとも別のヒープ マネージャーを使用するのでしょうか。また、共有メモリ内のグローバル データはどうでしょうか。どこにありますか。アプリケーションの場合、bss およびデータ セグメントにあることはわかっていますが、共有オブジェクト ファイルの場合はどこにあるかわかりません。
ベストアンサー1
私の質問は、これらのライブラリのコードがメイン アプリケーションと同じヒープ内にメモリを割り当てるのか、それとも独自のヒープを使用するのかということです。
ライブラリがmalloc/free
アプリケーションと同じものを使用する場合 (例: からglibc
)、はい、プログラムとすべてのライブラリは単一のヒープを使用します。
ライブラリをmmap
直接使用すると、プログラム自体が使用するメモリではないメモリを割り当てることができます。
たとえば、.so ファイル内の一部の関数が malloc を呼び出す場合、アプリケーションと同じヒープ マネージャーを使用するのでしょうか、それとも別のヒープ マネージャーを使用するのでしょうか?
.soの関数がmallocを呼び出す場合、このmallocはプログラムから呼び出されたmallocと同じです。Linux/glibc(>2.1)でシンボルバインディングログを見ることができます。
LD_DEBUG=bindings ./your_program
はい、ヒープ マネージャーの複数のインスタンス (デフォルト構成) は、互いを認識しなければ共存できません (問題は、インスタンス間で brk 割り当てヒープ サイズの同期を維持することです)。ただし、複数のインスタンスが共存できる構成は可能です。
従来の malloc 実装のほとんど (ptmalloc*、dlmalloc など) は、システムからメモリを取得する 2 つの方法を使用できます:brk
とmmap
。Brk は従来のヒープであり、線形で拡大または縮小できます。Mmap を使用すると、どこからでも大量のメモリを取得できます。また、このメモリを任意の順序でシステムに戻す (解放する) ことができます。
malloc がビルドされると、brk メソッドを無効にすることができます。その場合、malloc はmmap
s のみを使用して線形ヒープをエミュレートするか、従来の線形ヒープを無効にして、すべての割り当てが不連続な mmap フラグメントから行われるようになります。
そのため、一部のライブラリは、たとえば無効にしmalloc
てコンパイルされたりbrk
、malloc 以外のメモリ マネージャを使用して独自のメモリ マネージャを持つことができます。このマネージャには、および以外の関数名が必要です。malloc
たとえばfree
、malloc1
およびfree1
またはは、動的リンカーにこの名前を表示/エクスポートしないでください。
また、共有メモリ内のグローバル データはどうでしょうか。どこにありますか? アプリケーションの場合、bss およびデータ セグメントにあることはわかっていますが、共有オブジェクト ファイルの場合はどこにあるかわかりません。
プログラムと .so の両方を ELF ファイルと同様に考える必要があります。すべての ELF ファイルには「プログラム ヘッダー」( readelf -l elf_file
) があります。ELF からメモリにデータをロードする方法は、プログラム ヘッダーのタイプによって異なります。タイプが「LOAD
」の場合、ファイルの対応する部分はmmap
メモリにプライベートにロードされます (Sic!)。通常、2 つの LOAD セグメントがあります。1 つ目は R+X (読み取り + 実行) フラグを持つコード用で、2 つ目は R+W (読み取り + 書き込み) フラグを持つデータ用です。セクション.bss
と.data
(グローバル データ) セクションは両方とも、書き込み可能フラグを持つ LOAD タイプのセグメントに配置されます。
実行可能ライブラリと共有ライブラリの両方に LOAD セグメントがあります。一部のセグメントには、memory_size > file_size があります。これは、セグメントがメモリ内で拡張されることを意味します。セグメントの最初の部分は ELF ファイルのデータで埋められ、2 番目の部分 (memory_size-file_size) はゼロ (セクション用) で埋められます*bss
。mmap(/dev/zero)
memset(0)
カーネルまたはダイナミック リンカーが ELF ファイルをメモリにロードする場合、共有については考慮されません。たとえば、同じプログラムを 2 回起動するとします。最初のプロセスは mmap を使用して ELF ファイルの読み取り専用部分をロードします。2 番目のプロセスは同じ mmap を実行します (aslr がアクティブな場合、2 番目の mmap は異なる仮想アドレスになります)。ページ キャッシュ (VFS サブシステム) の役割は、物理メモリにデータの単一のコピーを保持することです (COPY-on-WRITE、別名 COW を使用)。mmap は、各プロセスの仮想アドレスから単一の物理位置へのマッピングを設定するだけです。いずれかのプロセスがメモリ ページを変更すると、書き込み時に一意のプライベート物理メモリにコピーされます。
ロードコードは、 ld.so の場合はglibc/elf/dl-load.c
( _dl_map_object_from_fd
)、linux-kernel/fs/binfmt_elf.c
カーネルの ELF ローダーの場合は ( elf_map
、load_elf_binary
) にあります。 を検索してくださいPT_LOAD
。
したがって、グローバル データと bss データは常に各プロセスでプライベートにマップされ、COW によって保護されます。
ヒープとスタックは、実行時に brk+mmap (ヒープ) で割り当てられ、OS カーネルによって brk のようなプロセスで自動的に割り当てられます (メイン スレッドのスタック用)。追加スレッドのスタックは、mmap
inを使用して割り当てられますpthread_create
。