今回はこちらの書籍「[試して理解]Linuxのしくみ〜実験と図解で学ぶOSとハードウェアの基礎知識」 の5章 メモリ管理 について、自己理解のために内容をかいつまんで要約します。素晴らしい書籍で理解が進むと思いますのでぜひご購入を検討ください。
メモリについて
Linuxではシステムに搭載されている全てのメモリをカーネルのメモリ管理システムで管理しています。メモリはユーザが立ち上げるプロセスやカーネルで利用します。システムでどのようにメモリを使っているのかを知りたい場合はfree
コマンドがオススメです。
以下の記事でfree
コマンドの見方を紹介しています。
Out of Memory(OOM)
プロセスを稼働したり作業を行ったりするとメモリが足りなくなることがあります。そのような状態になるとメモリ管理システムはカーネル内の解放可能なメモリ領域を解放します。(buffer/cache領域)
その後もメモリ使用量が増え続けるとメモリが足りない状態=Out of Memoryという状態になり、適当なプロセスを選んで強制終了するOOM Killerが発動します。特定のプロセスをOOM Killerの対象から外す方法もありますが難しいと思うので、カーネルパラメーターのvm.panic_on_oom
を1
にすることでOOM発生時にシステムの強制終了に変更することが可能です。
メモリの割り当て
Linuxではプロセスにメモリを割り当てる際に直接メモリを割り当てず仮想記憶というものを一枚噛ませます。そして仮想アドレスに物理メモリのアドレスをマッピングし、間接的にアクセスさせるようにします。
これにより以下のことが解決されます。
- メモリの断片化してしまう
- 別のプロセスのメモリへアクセスできてしまう
- マルチプロセスの扱いが困難になる
詳細は書籍をご覧ください。
ページテーブル
仮想アドレスと物理アドレスの変換はカーネルが使うメモリ内に保存されるページテーブルという表を使います。仮想記憶においては全てのメモリをページという単位で区切って管理しており、変換はページ単位で行われます。ページサイズはx86_64(64bit)の場合4KBです。
存在しない仮想アドレスにアクセスしたり、まだ物理アドレスに紐づけられていない仮想アドレスにアクセスするとページフォールトというエラーが発生します。
上位レイヤによるメモリの割り当て
プロセスがメモリを確保する際はmmap()関数
かmalloc()関数
のいずれかを呼び出しています。
関数名 | 動作 |
---|---|
mmap() |
ページ単位でメモリを確保する(64bitなら4KB単位) |
malloc() |
バイト単位でメモリを確保する |
使い勝手が良さそうに見えるmalloc()
ですが、その裏側はmmap()
を使っています。mmap()
によってあらかじめ大きめにメモリを確保しておき、malloc()
の請求に従って確保したメモリから要求されたバイト単位のメモリを切り出しています。そのためプロセス自体が把握している自分の使用メモリ量と、Linuxから見たプロセスの利用メモリ量は後者の方が大きく見えます。
仮想記憶の応用
ファイルマップ
ファイルマップとは**ファイルを仮想アドレス空間上にマップする機能です。
デマンドページング
mmap()システムコールによってメモリを割り当てる場合はそのままだと物理メモリを多く確保してしまいます。そこでデマンドページングの機能を用いて、実際に仮想アドレスにアクセスされたときにはじめて物理メモリを確保するようにします。
階層型ページテーブル
64bitのCPUでは最大128TBのメモリが扱えるためページテーブルも比例して巨大になります。そのためフラットなページテーブルではなく、少ないメモリでやりくりできるように階層型のページテーブルが用いられています。(x86_64のアーキテクチャでは4層になっている)
ヒュージページ
階層型ページテーブルの問題と合わせてページテーブルに使用する物理メモリが増えるとfork()システムコール
実行時の処理が遅くなります。これはfork()
では親プロセスのページテーブルを保存するためです。
ヒュージページを使うと大きなページサイズを扱えるようになりページテーブルに必要なメモリ量を減らすことができます。ページ数を減らすことでCPUの負荷を減らすことができ、スワップの対象にもなりません。(ページアウト/ページインしない)
まとめ
この章ではメモリ管理について整理しました。書籍にはより詳しい情報や、基礎的なため今回は端折った内容も豊富に掲載されていますので勉強の際はぜひお買い求めください。