私が見つけたこのQ&A共有メモリを使用して、プロセス間で共有ライブラリを共有できると言います。ただし、共有できるコードの種類をかなり厳しく制限することなく、プロセス間でコードを共有することは不可能です。私は、定義本文内のグローバルまたは静的変数の値によって出力が変わる再入不可能なC関数を持つライブラリを考えています。このように。
int really_really_nonreentrant(void x)
{
static int i = 0;
i++;
return i;
}
このような関数を持つライブラリは、それを使用するプロセスごとに別々の増分シーケンスを返すので、確かにコードのように見えます。いいえプロセス間で共有されます。 real_really_nonreentrant() は再入口関数から分離されていますか?それとも主に他の関数と一緒に保持され、static int iだけが分離されていますか?それとも、この関数は再入できないため、ライブラリ全体が共有メモリから除外されますか?
最終的にライブラリを共有メモリに割り当てるには、どのくらいの作業が必要ですか?
答え1
本当に短い答えは、Linuxコンパイラがコードをさまざまな部分に分割し、そのうちの少なくとも1つが純粋なコードであるため、メモリが複数のプロセスのアドレス空間にマッピングできることです。すべてのグローバル変数は、各プロセスが独自のコピーを持つようにマップされます。
、またはを使用してreadelf
これを見ることobjdump
ができますが、readelf
より明確な画像を提供すると思います。
これは出力の一部ですreadelf -e /usr/lib/libc.so.6
。これはおそらくほぼすべてのプロセスにマップされるCライブラリです。出力の関連部分readelf
(これはすべて面白いですが)はプログラムヘッダです。
Program Headers:
Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align
PHDR 0x000034 0x00000034 0x00000034 0x00140 0x00140 R E 0x4
INTERP 0x164668 0x00164668 0x00164668 0x00017 0x00017 R 0x4
[Requesting program interpreter: /usr/lib/ld-linux.so.2]
LOAD 0x000000 0x00000000 0x00000000 0x1adfc4 0x1adfc4 R E 0x1000
LOAD 0x1ae220 0x001af220 0x001af220 0x02c94 0x057c4 RW 0x1000
DYNAMIC 0x1afd90 0x001b0d90 0x001b0d90 0x000f8 0x000f8 RW 0x4
NOTE 0x000174 0x00000174 0x00000174 0x00044 0x00044 R 0x4
TLS 0x1ae220 0x001af220 0x001af220 0x00008 0x00048 R 0x4
GNU_EH_FRAME 0x164680 0x00164680 0x00164680 0x06124 0x06124 R 0x4
GNU_STACK 0x000000 0x00000000 0x00000000 0x00000 0x00000 RW 0x10
GNU_RELRO 0x1ae220 0x001af220 0x001af220 0x01de0 0x01de0 R 0x1
2 つの LOAD 行は、メモリに直接マップされるファイルの唯一の部分です。最初のLOADヘッダーは、RおよびE権限(読み取りおよび実行)を使用してブロックを/usr/lib/libc.so.6
メモリにマップします。これがコードです。ハードウェア機能は、プログラムがそのメモリに書き込むのを防ぎ、すべてのプログラムが物理物理メモリの同じページを共有できるようにします。カーネルは、同じ物理メモリをすべてのプロセスにマッピングするようにハードウェアを設定できます。
2番目のLOADヘッダーはRW(読み取りと書き込み)で表されます。 C ライブラリで使用されるグローバル変数の一部です。各プロセスは物理メモリに独自のコピーを持ち、そのプロセスのアドレス空間にマッピングされ、読み取りおよび書き込みを許可するように設定されたハードウェア権限を持ちます。このセクションは共有されません。
ファイルシステムを使用して、実行中のプロセス内でこれらのメモリマップを表示できます/proc
。説明する良いコマンドは次のとおりです。cat /proc/self/maps
。プロセスが所有するすべてのメモリマッピングと、カーネルがcat
それをインポートするファイルを一覧表示します。
関数に他のプロセスにマップされたメモリーを割り当てるために実行する必要がある操作の量は、ほぼ完全にコンパイラーに提供するフラグによって異なります。 「.so」共有ライブラリのコードは、「場所に依存しない」コンパイルされます。位置に依存しないコードは、絶対アドレスからロードまたは書き込み後にジャンプするのではなく、現在の命令に対するオフセットを使用して変数のメモリ位置を参照し、現在の命令を基準にした位置にジャンプまたは分岐するなどの操作を実行します。絶対アドレスに。つまり、「RE」LOADブロック/usr/lib/libc.so
と「RW」ブロックは、各プロセスから同じ距離だけ離れたアドレスにのみロードできます。サンプルコードではstatic
、変数はそれを参照するコードを除いて常にページサイズの倍数以上であり、LOAD ELFヘッダーの提供方法によって常にプロセスのアドレス空間にロードされます。
「共有メモリ」という用語に関しては、「システムVプロセス間通信システム」に関連するユーザレベルの共有メモリシステムがあることに留意されたい。これは、複数のプロセスがメモリブロックを非常に明示的に共有する方法です。これを設定して正しく設定するのは非常に複雑であいまいです。ここで言う共有メモリは、どのユーザプロセスでもほとんど完全には見えません。サンプルコードが複数のプロセス間で共有される場所に依存しないコードで実行されている場合、または唯一のコピーである場合、サンプルコードは違いを知りません。