Linuxが共有ライブラリを管理する方法について疑問に思っています。 (実際には、2009年にリリースされた256MB RAMで動作するDebianベースのディストリビューションであるMaemo Fremantleについて話しています)。
LibQtCore.so.4にリンクし、そのシンボル(クラスと関数を使用)を使用する2つの実行可能ファイルがあるとします。簡単にするために、それらをa
およびb
と呼びます。両方の実行可能ファイルが同じライブラリにリンクしていると想定しています。
最初にa
を起動します。ライブラリをロードする必要があります。全体として読み込まれますか、それとも必要な部分のみメモリに読み込まれますか(各クラスを使用しないため、使用されるクラスに関するコードのみが読み込まれます)?
次に、b
を起動します。 a
がまだ実行されていると想定します。 b
もlibQtCore.so.4にリンクし、a
が使用するクラスの一部を使用しますが、a
が使用しないクラスも使用します。ライブラリは二重にロードされますか(a
の場合は個別に、b
の場合は個別に)?または、すでにRAMにある同じオブジェクトを使用します。 b
が新しいシンボルを使用せず、a
がすでに実行されている場合、RAMは増加しますか?(または、違いはわずかです)
注:私はあなたのマシンにメモリマッピングユニット(MMU)があると仮定します。 MMUを必要としないLinuxバージョン(µClinux)があり、この回答はそこでは適用されません。
MMUとは?それはハードウェア —プロセッサやメモリコントローラの一部。共有ライブラリのリンクを理解するには、MMUの仕組みを正確に理解する必要はありません。MMUでlogicalメモリアドレス(プログラムで使用されるアドレス)およびphysicalメモリアドレス(実際にメモリバス上に存在するアドレス)メモリは、 Linuxでは通常4Kのサイズのページです。4Kページの場合、論理アドレス0〜4095はページ0、論理アドレス4096〜8191はページ1などです。MMUはこれらの物理ページにマップしますRAM、および各論理ページは通常0または1の物理ページにマップできます。特定の物理ページは複数の論理ページに対応できます(これがメモリの共有方法です。複数の論理ページが同じ物理ページに対応しています)。これは関係なく適用されますOSの;それはハードウェアの説明です。
プロセススイッチでは、カーネルがMMUページマッピングを変更するため、各プロセスには独自のスペースがあります。プロセス1000のアドレス4096は、プロセス1001のアドレス4096と完全に異なる場合があります(通常は異なります)。 。
ほとんどの場合、アドレスは論理アドレスです。ユーザー空間プログラムは、物理アドレスをほとんど扱いません。
現在、ライブラリを構築する方法もいくつかあります。プログラムがライブラリの関数foo()
を呼び出すとしましょう。 CPUはシンボルや関数呼び出しについて何も認識していません。論理アドレスにジャンプし、そこで見つけたコードを実行する方法を知っているだけです。これを行うにはいくつかの方法があります(ライブラリが独自のグローバルデータにアクセスする場合などにも同様のことが当てはまります)。
少なくとも汎用システムでは、だれも#1をもう使用していません。 32ビットシステムでは一意の論理アドレスリストを維持することは不可能で(回避するのに十分ではありません)、64ビットシステムでは管理上の悪夢になります。ただし、事前リンクは、システムごとに行われます。
#2と#3のどちらを使用するかは、ライブラリがGCCの-fPIC
(位置独立コード)オプションでビルドされているかどうかによって異なります。 #2はなし、#3はあり。通常、ライブラリは-fPIC
を使用して構築されるため、#3が実行されます。
詳細については、Ulrich Drepperの 共有ライブラリの作成方法(PDF) を参照してください。
最後に、あなたの質問に答えることができます:
-fPIC
(ほぼ確実にそうである)で構築されている場合、ページの大部分はすべてのプロセスでまったく同じですそれをロードします。プロセスa
とb
は、ライブラリを異なる論理アドレスでロードする可能性がありますが、これらは同じ物理ページをポイントします。メモリは共有されます。さらに、RAMのデータはディスク上のデータと正確に一致するため、ページフォールトハンドラーが必要な場合にのみロードできます。-fPIC
で構築されている場合、ライブラリのほとんどのページでリンクの編集が必要であり、異なることがわかります。したがって、それらは別々の物理ページでなければなりません(異なるデータが含まれているため)。つまり、共有されていません。ページがディスク上のものと一致しないので、ライブラリ全体がロードされても驚かないでしょう。もちろん、後で(スワップファイルで)ディスクにスワップアウトすることもできます。pmap
ツールを使用して、または/proc
のさまざまなファイルを直接チェックして、これを調べることができます。たとえば、次は、2つの異なる新しく生成されたbc
sでのpmap -x
の(部分的な)出力です。 pmapによって表示されるアドレスは、通常、論理アドレスであることに注意してください。
pmap -x 14739
Address Kbytes RSS Dirty Mode Mapping
00007f81803ac000 244 176 0 r-x-- libreadline.so.6.2
00007f81803e9000 2048 0 0 ----- libreadline.so.6.2
00007f81805e9000 8 8 8 r---- libreadline.so.6.2
00007f81805eb000 24 24 24 rw--- libreadline.so.6.2
pmap -x 17739
Address Kbytes RSS Dirty Mode Mapping
00007f784dc77000 244 176 0 r-x-- libreadline.so.6.2
00007f784dcb4000 2048 0 0 ----- libreadline.so.6.2
00007f784deb4000 8 8 8 r---- libreadline.so.6.2
00007f784deb6000 24 24 24 rw--- libreadline.so.6.2
ライブラリが複数の部分にロードされていることがわかります。pmap -x
は、それぞれの詳細を個別に提供します。 2つのプロセス間で論理アドレスが異なることがわかります。あなたはそれらが同じであると合理的に期待します(同じプログラムが実行され、コンピューターは通常そのように予測できるため)が、意図的にそれらをランダム化する address space layout randomization と呼ばれるセキュリティ機能があります。
サイズ(Kバイト)と常駐サイズ(RSS)の違いから、ライブラリセグメント全体がロードされていないことがわかります。最後に、より大きなマッピングの場合、dirtyは0であることがわかります。これは、ディスク上のものに正確に対応していることを意味します。
pmap -XX
を使用して再実行すると、-XX出力はカーネルバージョンによって異なるため、実行中のカーネルバージョンに応じて、最初のマッピングがShared_Clean
176、これはRSS
と完全に一致します。 Shared
メモリは、物理ページが複数のプロセス間で共有されることを意味します。これはRSSと一致するため、メモリ内のすべてのライブラリが共有されることを意味します(共有と共有の詳細については、以下の「関連項目」を参照してください)。民間):
pmap -XX 17739
Address Perm Offset Device Inode Size Rss Pss Shared_Clean Shared_Dirty Private_Clean Private_Dirty Referenced Anonymous AnonHugePages Swap KernelPageSize MMUPageSize Locked VmFlagsMapping
7f784dc77000 r-xp 00000000 fd:00 1837043 244 176 19 176 0 0 0 176 0 0 0 4 4 0 rd ex mr mw me sd libreadline.so.6.2
7f784dcb4000 ---p 0003d000 fd:00 1837043 2048 0 0 0 0 0 0 0 0 0 0 4 4 0 mr mw me sd libreadline.so.6.2
7f784deb4000 r--p 0003d000 fd:00 1837043 8 8 8 0 0 0 8 8 8 0 0 4 4 0 rd mr mw me ac sd libreadline.so.6.2
7f784deb6000 rw-p 0003f000 fd:00 1837043 24 24 24 0 0 0 24 24 24 0 0 4 4 0 rd wr mr mw me ac sd libreadline.so.6.2
関連項目