Linux オペレーティング システムでは、仮想アドレス空間の内部は カーネル空間とユーザー空間に分割されており、桁が異なるシステムではアドレス空間の範囲も異なります。たとえば、最も一般的な 32 ビット システムと 64 ビット システムは次のとおりです。
ここから見ることができます:
カーネル空間とユーザー空間の違いについて話しましょう:
各プロセスには独自の独立した仮想メモリがありますが、各仮想メモリ内のカーネル アドレスは実際には同じ物理メモリに関連付けられています。このようにして、プロセスがカーネル状態に切り替わった後、カーネル空間メモリに簡単にアクセスできます。
次に、仮想空間の分割について詳しく学びましょう。ユーザー空間とカーネル空間はさまざまな方法で分割されます。カーネル空間の配分については多くは語りません。
ユーザー空間の分布を見てみましょう。32 ビット システムを例として、それらの関係を示す図を描きました。
この図から、ユーザー空間メモリが低位から高位まで 6 つの異なるメモリ セグメントに分割されていることがわかります。:
プログラム ファイル セグメント (バイナリ実行可能コードを含む);ユーザーが割り当てたメモリが 128 KB 未満の場合は、brk();#「」
malloc() はどのような状況で brk() を通じてメモリを割り当てますか? mmap() を通じてメモリが割り当てられるのはどのようなシナリオですか?
」
#malloc() には、ソース コードでデフォルトで定義されたしきい値があります:
malloc() は物理メモリを割り当てますか?
いいえ、
malloc() は仮想メモリを割り当てます。 割り当てられた仮想メモリにアクセスしない場合、仮想メモリは物理メモリにマッピングされないため、物理メモリを占有しません。
割り当てられた仮想アドレス空間にアクセスする場合のみ、オペレーティング システムはページ テーブルを検索し、仮想メモリに対応するページが物理メモリにないことを検出し、ページ フォールト割り込みをトリガーし、オペレーティング システムは仮想メモリと物理メモリ、メモリ間のマッピング関係を確立します。
malloc(1) はどのくらいの仮想メモリを割り当てますか?として事前に割り当てます。 事前に割り当てられる特定のスペースの量は、malloc によって使用されるメモリ マネージャーに関連しています。分析には、malloc のデフォルトのメモリ マネージャー (Ptmalloc2) を使用します。
次に、次のコードを使用して実験を行い、malloc を通じて 1 バイトのメモリを申請したときにオペレーティング システムによって実際に割り当てられるメモリ領域の量を確認してみましょう。
リーリー実行コード (
使用する glibc ライブラリのバージョンが 2.17であることを事前に説明しておきます):
/proc//maps ファイルを通じてプロセスのメモリ分布を確認できます。この 1 バイトのメモリ開始アドレスによって、マップ ファイル内のメモリ アドレスの範囲をフィルタリングします。 リーリーこの例で割り当てられたメモリは 128 KB 未満であるため、メモリは brk() システム コールを通じてヒープ領域に適用され、右端に [heap] マークが表示されます。
ご覧のとおり、ヒープ領域のメモリ アドレス範囲は 00d73000 ~ 00d94000 で、この範囲のサイズは 132KB です。これは、
malloc(1) が実際に 132K バイトのメモリを事前に割り当てることを意味します## #。一部の学生は、プログラムで出力されたメモリの開始アドレスが d73010 であり、マップ ファイルではヒープ メモリ空間の開始アドレスが d73000 であることに気づいたかもしれません。なぜ余分な 0x10 (16 バイト) があるのでしょうか?この質問は今は脇に置いておいて、後で説明します。
#free はメモリを解放してオペレーティング システムに戻しますか?free() 関数でメモリを解放した後、上記のプロセスを実行してヒープ メモリがまだあるかどうかを確認してみましょう。
下の図からわかるように、メモリを解放した後もヒープ メモリはまだ存在しており、オペレーティング システムに返されていません。
これは、この 1 バイトをオペレーティング システムに解放するのではなく、キャッシュして malloc のメモリ プールに入れる方が良いためです。プロセスが 1 バイトのメモリを再度適用すると、この速度ははるかに高速です。
もちろん、プロセスが終了すると、オペレーティング システムはプロセスのすべてのリソースを再利用します。上記の空きメモリは、brk() メソッドを介して malloc によって適用されたメモリ用であり、ヒープ メモリはまだ存在します。
malloc が mmap を通じてメモリに適用された場合、free がメモリを解放した後、メモリがオペレーティング システムに返されます。
malloc を通じて 128 KB のメモリを申請し、malloc が mmap を通じてメモリを割り当てることを検証する実験を行ってみましょう。
リーリー
実行コード:プロセスのメモリ分布を見ると、右端に[先頭]マークがなく、ファイルマッピング領域からmmap経由で匿名マッピングにより匿名メモリが割り当てられていることがわかります。
それでは、このメモリを解放して見てみましょう:
128 KB メモリの開始アドレスを再度確認すると、メモリがもう存在していないことがわかり、オペレーティング システムに返されたことがわかります。
「malloc によって要求され、free によって解放されたメモリはオペレーティング システムに返されますか?」という質問については、次のように要約できます。
したがって、メモリを確保する操作は頻繁にシステム コールを行わないようにする必要があり、mmap を使用してメモリを確保すると、毎回システム コールを実行する必要があります。
また、mmap によって割り当てられたメモリは解放されるたびにオペレーティング システムに返されるため、mmap によって割り当てられた仮想アドレスは毎回ページ フォールト状態になり、仮想アドレスにアクセスするたびにページ フォルト状態になります。初めて、ページフォルト割り込みがトリガーされます。
つまり、
mmapで確保したメモリを頻繁に使用すると、その都度実行状態が切り替わるだけでなく、(仮想アドレスへの最初のアクセス後)ページフォルト割り込みも発生します。これにより CPU が消費されます。 が大きいほど。
これら 2 つの問題を改善するために、malloc が brk() システム コールを通じてヒープ領域のメモリに適用するとき、ヒープ領域は連続しているため、より大きなメモリをメモリ プールとして直接事前に割り当てます。解放されると、メモリ プールにキャッシュされます。
次回メモリを申請する際には、該当するメモリブロックをメモリプールから直接取り出すだけで、このメモリブロックの仮想アドレスと物理アドレスのマッピング関係が残っている可能性があります。システムの削減 呼び出しの数により、ページ フォールト割り込みの数も削減され、CPU 消費量が大幅に削減されます。
brk は非常に素晴らしいので、すべての割り当てに brk を使用してみてはいかがでしょうか?10k、20k、30k の 3 つのメモリを連続して申請した場合、10k と 20k が解放されると空きメモリ領域になります。次回申請されるメモリが 30k 未満であれば、この空きメモリは使用できます。再利用できるスペース。
しかし、次回要求されるメモリが 30k を超える場合は、空きメモリ容量がなくなり、OS に申請する必要があり、実際に使用されるメモリは増加し続けます。したがって、システムが特に小さなメモリ ブロックに対して頻繁にマロックと解放を行うと、ヒープ内に使用できないフラグメントがどんどん生成され、「メモリ リーク」が発生します。この「漏れ」現象は、valgrind では検出できません。
したがって、malloc の実装では、brk と mmap の動作の違い、利点、欠点が十分に考慮され、mmap がメモリ空間を割り当てる前に、デフォルトで大きなメモリ ブロック (128KB) が割り当てられます。
malloc によってユーザー モードに返されるメモリの開始アドレスは、プロセスのヒープ領域の開始アドレスより 16 バイト大きいと前に述べたことを覚えていますか?
追加の 16 バイトには、メモリ ブロックのサイズなど、メモリ ブロックの説明情報が格納されます。
このようにして、free() 関数が実行されると、free は受信したメモリ アドレスを 16 バイト左にオフセットし、この 16 バイトから現在のメモリ ブロック サイズを分析します。どれだけのメモリを解放するか。
以上がLinux のメモリ割り当て戦略を 1 つの記事で理解するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。