この記事の内容は、Linux メモリ管理の関連知識を誰もが理解できるように、Linux メモリ管理を紹介することです。一定の参考値があるので、困っている友人は参考にしていただければ幸いです。
Linux では、top、vmstat、free などのコマンドを使用してシステムまたはプロセスのメモリ使用量を確認すると、buff/cache memeory、swap、avail Mem などがよく表示されます。これらは何を意味するのでしょうか。 ?この記事では、Linux でのメモリ管理について説明し、この質問に答えます。
Linux でのメモリ管理について議論するということは、実際には Linux での仮想メモリの実装について議論することになります。私はカーネルの専門家ではないので、この記事では概念的なことだけを紹介し、実装の詳細については触れません。正確ではありません。
初期の頃、物理メモリは比較的限られていました。人々は、プログラムが使用できるメモリ空間が実際の物理メモリを超えることを期待し、仮想メモリの概念が登場しました。しかし、時間が経つにつれて、仮想メモリの意味は当初の考えをはるかに超えて変化しました。
仮想メモリは、Linux がメモリを管理するために使用するテクノロジです。これにより、各アプリケーションは、独立した連続的な使用可能なメモリ空間 (連続的で完全なアドレス空間) があると認識しますが、実際には、通常、複数の物理メモリ セグメントにマップされ、一部は一時的に外部に保存され、ディスク ストレージにロードされます。必要なときにメモリに保存されます。
各プロセスが使用できる仮想アドレスのサイズは、CPU ビット数に関係します。32 ビット システムでは、仮想アドレス空間のサイズは 4G ですが、64 ビット システムでは、 2^64=? (理解できません)。実際の物理メモリは、仮想アドレス空間のサイズよりもはるかに小さい場合があります。
仮想アドレスはプロセスと密接に関係しています。異なるプロセスの同じ仮想アドレスが必ずしも同じ物理アドレスを指すとは限らないため、プロセスを離れずに仮想アドレスについて話すのは意味がありません。
注: インターネット上の多くの記事では、仮想メモリとスワップ スペースを同一視しています。実際、その説明は十分に厳密ではありません。スワップ スペースは、仮想メモリの大きな設計図の一部にすぎません。
#2. 仮想メモリと物理メモリの関係
次の表は、それらの間の関係を非常に直感的に表しています
进程X 进程Y +-------+ +-------+ | VPFN7 |--+ | VPFN7 | +-------+ | 进程X的 进程Y的 +-------+ | VPFN6 | | Page Table Page Table +-| VPFN6 | +-------+ | +------+ +------+ | +-------+ | VPFN5 | +----->| .... |---+ +-------| .... |<---+ | | VPFN5 | +-------+ +------+ | +------+ | +------+ | | +-------+ | VPFN4 | +--->| .... |---+-+ | PFN4 | | | .... | | | | VPFN4 | +-------+ | +------+ | | +------+ | +------+ | | +-------+ | VPFN3 |--+ | | .... | | | +--->| PFN3 |<---+ +----| .... |<---+--+ | VPFN3 | +-------+ | | +------+ | | | +------+ | +------+ | +-------+ | VPFN2 | +-+--->| .... |---+-+-+ | PFN2 |<------+ | .... | | | VPFN2 | +-------+ | +------+ | | +------+ +------+ | +-------+ | VPFN1 | | | +----->| FPN1 | +----| VPFN1 | +-------+ | | +------+ +-------+ | VPFN0 |----+ +------->| PFN0 | | VPFN0 | +-------+ +------+ +-------+ 虚拟内存 物理内存 虚拟内存 PFN(the page frame number): 页编号
プロセスがプログラムを実行するとき、最初にメモリからプロセスの命令を読み取ってから実行する必要があります。仮想命令は、アドレス、このアドレスはプログラムのリンク時に決定されます (ダイナミック ライブラリのアドレス範囲は、カーネルがプロセスをロードして初期化するときに調整されます)。実際のデータを取得するには、CPU が仮想アドレスを変換する必要があります。 CPU がアドレスを変換するときに必要な物理アドレスにプロセスのページ テーブルとページ テーブル内のデータはオペレーティング システムによって維持されます。
注: Linux カーネル コードはメモリにアクセスするときに実際の物理アドレスを使用するため、仮想アドレスから物理アドレスへの変換は行われず、アプリケーション層プログラムのみが変換を必要とします。
変換を容易にするために、Linux は仮想メモリと物理メモリの両方を固定サイズのページに分割します。x86 システムの一般的なメモリ ページ サイズは 4K で、各ページには一意の番号が割り当てられます。これはページ番号 (PFN) です。
上の図からわかるように、仮想メモリ ページと物理メモリ ページ間のマッピングはページ テーブルを通じて行われます。プロセスXとプロセスYの仮想メモリは独立しており、ページテーブルも独立しており、物理メモリは共有されています。プロセスは独自の仮想アドレス空間に自由にアクセスできますが、ページ テーブルと物理メモリはカーネルによって維持されます。プロセスがメモリにアクセスする必要がある場合、CPU はプロセスのページ テーブルに基づいて仮想アドレスを物理アドレスに変換し、それにアクセスします。
注: 仮想アドレス空間内のすべてのページが、対応するページ テーブルに関連付けられるわけではありません。仮想アドレスがプロセスに割り当てられた後、つまり、プロセスが malloc に似た関数を呼び出した後のみです。 , システムは、対応する仮想アドレスのページ テーブルにレコードを追加します。プロセスがページ テーブルに関連付けられていない仮想アドレスにアクセスすると、システムは SIGSEGV シグナルをスローし、プロセスが終了します。これが理由です。セグメントフォールトは、ワイルド ポインターにアクセスするときによく発生します。つまり、各プロセスは 4G (32 ビット システム) の仮想アドレス空間を持っていますが、システムに適用されているアドレス空間しか使用できず、未割り当てのアドレス空間にアクセスするとセグメントフォルト エラーが発生します。 Linux は仮想アドレス 0 をどこにもマップしないため、null ポインターにアクセスすると、セグメントフォールト エラーが確実に報告されます。
● より大きなアドレス空間: 連続的であるため、書き込みとリンクが容易になります。プログラム よりシンプル
# プロセスの分離: 異なるプロセスの仮想アドレス間には関係がないため、1 つのプロセスの操作が他のプロセスに影響を与えることはありません
#● データ保護: 仮想の各部分メモリには、対応する読み取り属性と書き込み属性があり、プログラムのコード セグメントの変更やデータ ブロックの実行などを保護でき、システムのセキュリティが向上します●メモリ マッピング: 仮想メモリを使用すると、ディスク上のファイル (実行可能ファイルまたはダイナミック ライブラリ) を仮想アドレス空間に直接マッピングできます。これにより、対応するファイルが必要な場合にのみ、物理メモリの遅延割り当てを実現できます。 read メモリが不足している場合、実際にはディスクからメモリにロードされます。メモリが不足している場合、メモリのこの部分をクリアして物理メモリの利用効率を向上させることができ、これはすべてアプリケーションに対して透過的です。
# 共有メモリ: たとえば、ダイナミック ライブラリはメモリにコピーを保存し、それを別のプロセスの仮想アドレス空間にマップするだけで済み、プロセスは自分がメモリの排他的所有権を持っていると感じます。ファイル。プロセス間でのメモリ共有は、同じ物理メモリをプロセスの異なる仮想アドレス空間にマッピングすることによっても実現できます。
# 物理メモリ管理: 物理アドレス空間はすべてオペレーティング システムによって管理され、プロセスを制御することはできません。これにより、システムはメモリをより有効に活用し、プロセス間のメモリ要件のバランスをとることができます。
#●その他: 仮想アドレス空間を使用すると、スワップ スペースや COW (コピー オン ライト) などの機能が利用できます。 ) は簡単に実装できます上記の説明を通じて、メモリ使用プロセスを次のように簡単に要約できます:
1. プロセスはメモリ アプリケーション リクエストをシステムに送信します
2. システムはメモリ アプリケーション リクエストをチェックします。プロセスの仮想アドレス スペースは使い果たされていますか? 残っている場合は、仮想アドレスをプロセスに割り当てます
3。システムは、この仮想アドレスに対応するメモリ マッピング (場合によっては複数) を作成し、それをプロセスのページ テーブル。
4。システムは仮想アドレスをプロセスに返し、プロセスは仮想アドレス
5 へのアクセスを開始します。CPU は、対応するメモリ マッピングを検索します。仮想アドレスに基づくプロセスのページ テーブルですが、マッピングは物理メモリに関連付けられていないため、ページ フォールト割り込みが発生します
6. オペレーティング システムはページ フォールト割り込みを受信した後、実メモリを割り当てます。物理メモリを作成し、それを対応するメモリ マッピングに関連付けます
7。割り込み処理が完了すると、CPU はメモリにアクセスできるようになります
もちろん、ページ欠落割り込みは毎回発生するわけではありません。システムがメモリの割り当てを遅らせる必要があると判断した場合にのみ使用されます。つまり、上記のステップ 3 で何度も使用されます。システムは実際の物理メモリを割り当て、メモリ マッピングに関連付けます。
オペレーティング システムが仮想メモリと物理メモリの間のマッピング関係を認識している限り、オペレーティング システムは正常に動作します。ただし、メモリ アクセスをより効率的にするには、考慮すべきことがまだたくさんあります。ここでは、メモリとその機能に関連する他の概念をいくつか見ていきます。
MMU は、プロセスの仮想アドレスを物理アドレスに変換するために使用される CPU のモジュールです。 . シンプル 簡単に言うと、このモジュールの入力はページ テーブルとプロセスの仮想アドレス、出力は物理アドレスです。仮想アドレスを物理アドレスに変換する速度はシステムの速度に直接影響するため、CPU には高速化のためにこのモジュールが組み込まれています。
上で紹介したように、MMU の入力はページ テーブルであり、ページ テーブルが格納されます。メモリの速度は CPU のキャッシュに比べて非常に遅いため、仮想アドレスから物理アドレスへの変換速度をさらに高速化するために、Linux は CPU の L1 キャッシュに存在する TLB を発明しました。見つかった仮想アドレスを物理アドレスにキャッシュするために使用されます。マッピングなので、次の変換の前に TLB を確認してください。既に TLB に含まれている場合は、MMU を呼び出す必要はありません。
実際の状況では、物理メモリは仮想メモリよりもはるかに少ないため、オペレーティング システムはメモリ使用量を最大化するために非常に慎重に物理メモリを割り当てる必要があります。物理メモリを節約する 1 つの方法は、現在使用されている仮想ページに対応するデータのみをメモリにロードすることです。たとえば、大規模なデータベース プログラムでクエリ操作のみを使用する場合、挿入、削除などを行うコード セグメントをメモリにロードする必要はなく、物理メモリを大幅に節約できます。この方法は次のとおりです。オンデマンド割り当ては、遅延ロードとも呼ばれます。
実装原理は非常に単純です。つまり、CPU が仮想メモリ ページにアクセスするときに、仮想メモリ ページに対応するデータが物理メモリにロードされていない場合、CPU はオペレーティング システムに通知します。ページ フォールトが発生した場合、オペレーティング システムがデータを物理メモリにロードする必要があります。データをメモリにロードするのには時間がかかるため、CPU はそこで待機せずに他のプロセスをスケジュールし、次回プロセスをスケジュールするときには、データはすでに物理メモリに存在しています。
Linux は主に、実行可能ファイルと動的ライブラリをロードするためにこのメソッドを使用します。プログラムがカーネルによって実行されるようにスケジュールされ始めると、カーネルはプロセスの実行可能ファイルと動的ライブラリを仮想アドレス空間にマップします。プロセスを実行し、すぐに使用されるデータのごく一部のみを物理メモリにロードし、他の部分は CPU がアクセスしたときにのみロードされます。
プロセスがデータを物理メモリにロードする必要があるが、実際の物理メモリが使い果たされている場合、オペレーティング システムは現在のプロセスのニーズを満たすために、物理メモリ内の一部のページを再利用する必要があります。
ファイルでバックアップされたメモリ データの場合、つまり、物理メモリ内のデータがディスク上のファイルから取得される場合、カーネルはデータのこの部分をメモリから直接削除して、より多くのメモリを解放します。プロセスがデータのこの部分にアクセスする必要がある場合、データはディスクからメモリにロードされます。ただし、データのこの部分が変更されていてファイルに書き込まれていない場合、データのこの部分はダーティ データになります。ダーティ データは直接削除できず、スワップ領域に移動することしかできません。 (実行可能ファイルとダイナミック ライブラリ ファイルは変更されませんが、mmap private を通じてメモリにマップされたディスク ファイルは変更される可能性があります。この方法でマップされたメモリは非常に特殊です。変更前はファイルにバックアップされていましたが、変更後はそうではありませんでした。ディスクに書き戻す前に匿名になります)
对于anonymous的内存数据,在磁盘上没有对应的文件,这部分数据不能直接被删除,而是被系统移到交换空间上去。交换空间就是磁盘上预留的一块特殊空间,被系统用来临时存放内存中不常被访问的数据,当下次有进程需要访问交换空间上的数据时,系统再将数据加载到内存中。由于交换空间在磁盘上,所以访问速度要比内存慢很多,频繁的读写交换空间会带来性能问题。
关于swap空间的详细介绍请参考Linux交换空间
有了虚拟内存之后,进程间共享内存变得特别的方便。进程所有的内存访问都通过虚拟地址来实现,而每个进程都有自己的page tables。当两个进程共享一块物理内存时,只要将物理内存的页号映射到两个进程的page table中就可以了,这样两个进程就可以通过不同的虚拟地址来访问同一块物理内存。
从上面的那个图中可以看出,进程X和进程Y共享了物理内存页PFN3,在进程X中,PFN3被映射到了VPFN3,而在进程Y中,PFN3被映射到了VPFN1,但两个进程通过不同的虚拟地址访问到的物理内存是同一块。
page table里面的每条虚拟内存到物理内存的映射记录(memory mapping)都包含一份控制信息,当进程要访问一块虚拟内存时,系统可以根据这份控制信息来检查当前的操作是否是合法的。
为什么需要做这个检查呢?比如有些内存里面放的是程序的可执行代码,那么就不应该去修改它;有些内存里面存放的是程序运行时用到的数据,那么这部分内存只能被读写,不应该被执行;有些内存里面存放的是内核的代码,那么在用户态就不应该去执行它;有了这些检查之后会大大增强系统的安全性。
由于CPU的cache有限,所以TLB里面缓存的数据也有限,而采用了huge page后,由于每页的内存变大(比如由原来的4K变成了4M),虽然TLB里面的纪录数没变,但这些纪录所能覆盖的地址空间变大,相当于同样大小的TLB里面能缓存的映射范围变大,从而减少了调用MMU的次数,加快了虚拟地址到物理地址的转换速度。
为了提高系统性能,Linux使用了一些跟内存管理相关的cache,并且尽量将空闲的内存用于这些cache。这些cache都是系统全局共享的:
Buffer Cache
用来缓冲块设备上的数据,比如磁盘,当读写块设备时,系统会将相应的数据存放到这个cache中,等下次再访问时,可以直接从cache中拿数据,从而提高系统效率。它里面的数据结构是一个块设备ID和block编号到具体数据的映射,只要根据块设备ID和块的编号,就能找到相应的数据。
Page Cache
这个cache主要用来加快读写磁盘上文件的速度。它里面的数据结构是文件ID和offset到文件内容的映射,根据文件ID和offset就能找到相应的数据(这里文件ID可能是inode或者path,本人没有仔细去研究)。
从上面的定义可以看出,page cache和buffer cache有重叠的地方,不过实际情况是buffer cache只缓存page cache不缓存的那部分内容,比如磁盘上文件的元数据。所以一般情况下和page cache相比,Buffer Cache的大小基本可以忽略不计。
当然,使用cache也有一些不好的地方,比如需要时间和空间去维护cache,cache一旦出错,整个系统就挂了。
有了上面介绍的知识,再来看看我们刚开始提出来的问题,以top命令的输出为例:
KiB Mem : 500192 total, 349264 free, 36328 used, 114600 buff/cache KiB Swap: 524284 total, 524284 free, 0 used. 433732 avail Mem
KiB Mem代表物理内存,KiB Swap代表交换空间,它们的单位都是KiB。
total、used和free没什么好介绍的,就是总共多少,然后用了多少,还剩多少。
buff/cached代表了buff和cache总共用了多少,buff代表buffer cache占了多少空间,由于它主要用来缓存磁盘上文件的元数据,所以一般都比较小,跟cache比可以忽略不计;cache代表page cache和其它一些占用空间比较小且大小比较固定的cache的总和,基本上cache就约等于page cache,page cache的准确值可以通过查看/proc/meminf中的Cached得到。由于page cache是用来缓存磁盘上文件内容的,所以占有空间很大,Linux一般会尽可能多的将空闲物理内存用于page cache。
avail Mem は、プロセスの次の割り当てに使用できる物理メモリの量を示します。システムは空き領域に加えて、一部の領域もすぐに解放できるため、このサイズは通常、空きよりも少し大きくなります。
では、現在のメモリ使用量が異常かどうかを判断するにはどうすればよいでしょうか?
# Mem free の値が比較的小さく、buff/cache の値も小さい
free の値が比較的小さいからといって、必ずしも存在するわけではありません。 Linux はページ キャッシュにメモリが使用されるように最善を尽くしますが、buff/cache の値も小さい場合は、メモリが不足しており、システムにキャッシュ用の十分なメモリがないことを意味します。サーバー展開は、FTP サーバーなど、頻繁にディスクの読み取りと書き込みを必要とするアプリケーションの場合、パフォーマンスへの影響が非常に大きくなります。
この状況は上記よりも深刻です。通常の状況では、スワップはほとんど使用されません。使用済みの値が比較的大きい場合は、スワップ領域が使用されていることを示しますvmstat コマンドでスワップ イン/アウトが頻繁に発生する場合は、システム メモリが深刻に不足しており、全体的なパフォーマンスが深刻な影響を受けていることを意味します。
Linux チュートリアル#」 ##" 以上がこの記事の全内容です。皆様の学習のお役に立てれば幸いです。さらにエキサイティングなコンテンツについては、PHP 中国語 Web サイトの関連チュートリアルのコラムに注目してください。 ! !
以上がLinux メモリ管理の概要の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。