Linuxカーネルの大部分はC言語で記述されているため、カーネルがメインメモリに読み込まれると、標準CライブラリもLinuxカーネルに沿って読み込まれますか?
それがCで書かれたプログラムが他のプログラムよりも少ないメモリを消費する理由である場合、標準のCライブラリは既にロードされており、その結果、Linuxマシンで実行したときに他の言語で書かれたプログラムと比較して高速です(ページ違反が少ない)。
カーネルはCで書かれていますが、Cライブラリを使用していません( dave_thompson_085 が指摘しているように、「 freestanding 」)。たとえそうであったとしても、カーネルと共に使用するためにカーネルとともにロードされたCライブラリは、カーネルでのみ利用可能です(カーネルが何らかの方法でユーザー空間に明示的にアクセス可能にした場合を除く)。したがって、それは削減に役立ちませんプログラムのメモリ要件。
とは言っても、ほとんどの場合、最初のプログラムはカーネルの起動後に実行されます(initramfs内のプログラム、独自のCライブラリのコピーを使用しますが、最終的にはinit
)。Cライブラリを使用します。そのため、早い段階でマッピングされることになり、広く使用されているライブラリの部分は常に物理メモリに残る可能性が高くなります。
カーネルには Cライブラリの多くの関数 の実装、またはバリアント(たとえば、printk
の代わりに printf
)の実装が含まれています。しかし、すべてが基準に正確に従っているわけではありません。状況によっては、代わりにコンパイラのCライブラリ関数の実装が使用されます。
(C以外の言語で書かれたプログラムの大部分は、最終的にCライブラリを使用することに注意してください。)
Linuxのメインメモリには、標準Cライブラリがデフォルトでロードされていますか?
番号。
カーネルが開始する最初のユーザー空間プロセスはinit
と呼ばれ、その目的は他のすべてを開始し、ゾンビの子(親プロセスが終了する前に終了した親プロセスのプロセス)を取得することです。終了ステータス)。
init
はほとんどの場合標準Cライブラリを使用するため、init
が起動するとすぐに標準Cライブラリがメモリにロードされます。しかし、これは単なる副作用であり、カーネルが気にすることはありません。
カーネルは標準Cライブラリとは何の関係もありません。 (カーネルは、標準Cライブラリがnotが利用可能な環境であるfreestanding Cで記述されています。)
カーネルのコンテキストでの「C標準ライブラリ」は、より抽象的な形式です。つまり、C言語で指定されたように動作する、再利用可能なユーティリティサブルーチンがあります。 C言語ではなくカーネル自体によって定義されている再利用可能なサブルーチンと同様に、これらは「共有オブジェクト」(動的ライブラリ)形式では存在しません。代わりに、それらはカーネルと一緒にコンパイルされ、1つのカーネル実行可能ファイルにアセンブルされます。
ここにライブラリコードの一部を見つけることができます。 memcpy、strcmp/strncmp https://elixir.bootlin.com/linux/latest/source/Arch/x86/lib
そのため、カーネルは独自のCライブラリ実装とともに出荷され、それを他のユーザーランドコードと共有せず、ユーザーがブートするために提供するCライブラリを使用しません。実際、各ユーザーランドプログラムは同じようにすることができます。静的リンク(インクルード、パック)はCライブラリであり、他の誰とも共有しません。
最後の質問に答えるために、Cの標準ライブラリをカーネルと共有するユーザーランドプログラムはありません。
いいえ、カーネルは標準のCライブラリを使用しません。
標準Cライブラリ(通常、glibcはLinuxで使用されるもの)は、C呼び出しをカーネルのsyscallに「変換」していることに注意してください。多くのことはユーザーランドで完全に行われますが、カーネルに基づいて構築されています。したがって、バイナリ形式で、ユーザーランドと同様にカーネルでも使用できるCライブラリを使用するのはそれほど簡単ではありません。また、通常、ライブラリの目的はかなり異なります。
そして今、第二部。システム内のほぼすべてのLinuxプログラムが同じ標準Cライブラリにリンクしている場合でも、各プログラムはCライブラリ自体をロードします。
grep libc /proc/self/smaps
を数回呼び出すと、(ASLRの結果として)libcが毎回異なるアドレスにどのようにマップされるかを確認できます。これは、明示的にロードされていなくても、kernel32.dll( KnownDLL )などのいくつかのライブラリがすべてのプロセスの同じ場所にマップされているWindowsとは対照的です。
Linuxでは、プログラム(そうですね、ld)は、他のすべての動的ライブラリと同じようにlibcをロードする手順を実行する必要があります。 libcが最も最適化されたコードの1つであることは事実ですが、ロードと一般的な実行の両方が迅速です。多くの場合、非常に低レベル( lrich Drepperの記事をご覧ください )では、通常のプログラムには役立たないでしょう。
それがCで書かれたプログラムが他のプログラムよりも少ないメモリを消費する理由である場合
いいえ。メモリ空間はより少ないメモリを消費しません。 libcは、プロセス上のlibcのメモリ使用量を表示します。
標準Cライブラリがすでにロードされているため、結果としてより高速になります(ページフォールトが減ります)。
ただし、コードは既にメモリに読み込まれているため、ディスクからフェッチする必要はありません(ページごとに実行されますが、プログラムに必要なコードのセクションは、以前に別のセクションから要求された可能性があります)。
実際、Linuxはメモリ内のファイルを積極的にキャッシュします。十分なメモリがある場合、いったんプログラムをディスクからロードすると、プログラムはメモリにキャッシュされ、そのコードは(遅い)ディスクから再度ロードされません。したがって、事前にそれらをディスクからロードしたり、以前にロードしたりすることで、同じ効果を得ることができます。
linuxマシンで実行したときに他の言語で書かれたプログラムと比較して?
この小さな違いのために、より速くそれらを見つける可能性は低いです。より可能性が高いのは、次の理由だと思います。
他の言語がそれほど遅くない可能性もあります。それらを同等に比較するために、そのようなプログラムのペアを指定し、Cバージョンが本当に速いかどうか、そして実際に違いがある場所を特定する必要があります。
C標準ライブラリの短縮名はlibcです。
libcが他のライブラリと同じであることを最初に明らかにしましょう。
カーネル自体はlibcを使用しません( here を参照してください)。
libcは動的または静的にリンクできます。
動的リンク:
ライブラリは実行可能ファイルに埋め込まれていません。プログラムが起動すると、ライブラリはシステムパスから読み込まれます。たとえば、Linuxでは/ lib/x86_64-linux-gnu/libc.soです。
そしてここに来る:2番目のプログラムが起動した場合libcは再度読み込まれません。代わりに、最初のプログラムとライブラリを共有します。つまり、新しいメモリは必要ありません。
静的リンク:
libcが静的にリンクされている場合、ライブラリは実行可能ファイル内にあります。したがって、2つのプログラムを実行すると、libcはメモリに2回存在します。
Linux(およびそれ以降のバージョンのUnix)は共有ライブラリをサポートしています。これが大きな違いです。共有ライブラリはメモリに読み込まれ、読み取り専用です。別のプログラムが同じリソースをロードする場合、既存のメモリリソースへのリンクを取得するだけです。これが、共有メモリ(.so)が高速でメモリ使用量が少ない理由であり、静的にリンクされた(.aと考える)プログラムがあらゆる点で大きい理由です。 .aには利点がありますが、メモリ使用量とプログラムの起動速度はその中にありません。これはコードにのみ適用され、すべてユーザー空間にある変数には適用されません。標準ライブラリに関しては、プログラムをロードする可能性が早いのはlibc.soと他のほとんどすべてのプログラムを使用することです。つまり、それはメモリ内にあり、コードのコピーは1つだけ必要です。