プロセスが使うアドレス

virtual addressと物理RAMを分ける

プログラムが命令の中で使うのはvirtual addressvirtual addressプロセスのアドレス空間で、命令がメモリを参照するときに使うアドレス。です。CPUのMMUは、カーネルが用意したpage tablespage tablesvirtual addressと物理RAMの対応を記録する表。を参照し、物理RAM上の位置へ変換します。

メモリはpagepagevirtual memoryと物理RAMを管理する基本単位。単位で管理されます。x86-64では4 KiBが広く使われますが、pageの大きさはCPUアーキテクチャとカーネル設定によって変わります。

03.1
必要になった時点でRAMを用意する

page faultから処理を再開するまで

プロセスがアドレス範囲を確保しても、その全域へすぐ物理RAMを割り当てるとは限りません。最初のアクセスでpage faultpage fault現在のpage tablesだけではメモリアクセスを完了できないときにCPUが起こす例外。が起きると、Linuxがアクセスの正当性とpageの準備方法を判断します。

01

CPUがアクセス

命令がvirtual addressを読み書きします。

02

page fault

対応するpageがない場合や、アクセス権を確認する場合に例外が発生します。

03

Linuxが処理

RAMを割り当てる、ファイルから読む、共有pageを複製するなどの処理を選びます。

04

命令を再開

page tablesを更新し、完了できなかったメモリアクセスをもう一度実行します。

03.2
pageの内容によって回収方法が変わる

anonymous memoryとpage cache

heap / stack

anonymous memory

anonymous memoryanonymous memory特定のファイルを保存元として持たないメモリ。は、ファイルを保存元に持たないpageです。内容を残したままRAMを空ける場合はswapへ書き出します。

物理RAMswap
executable / file I/O

page cache

page cachepage cacheファイルの内容をRAMへ保持し、読み書きを速くする仕組み。は、ファイルの内容をRAMへ保持します。変更のないpageは破棄してもファイルから再読込できます。変更済みならストレージへ書き戻します。

物理RAMファイル
03.3
空きRAMが少なくなったとき

reclaimとMulti-Gen LRU

reclaimreclaim空きRAMを増やすため、使用中のpageを回収候補から選んで解放する処理。は、page cacheの破棄や書き戻し、anonymous memoryのswapを通じて空きRAMを増やします。頻繁に参照されるpageを回収すると、直後に再読込が必要になり性能が落ちるため、回収候補の選択が必要です。

新しいGen 3
Gen 2
Gen 1
古いGen 0
回収候補 →
Linux 6.1

Multi-Gen LRU

Multi-Gen LRUMulti-Gen LRUpageを参照時期の世代に分け、reclaimの候補を選ぶ仕組み。は、参照された時期に応じてpageを複数の世代へ分け、古い世代から回収候補を探します。pageの種類と参照状況を追跡し、最近使われたpageをRAMへ残しやすくします。

Googleによる開発の記録を見る
03.4
複数のpageをまとめて処理する

folio

page単位
folio単位

page cacheの処理単位を広げる

foliofolio連続する一つ以上のpageを、まとまりとして扱うための型。は、連続する一つ以上のpageをまとまりとして扱う型です。Linux 5.16で導入され、page cacheなどの処理で使われています。

1 pageだけを表すfolioもあります。複数のpageを含む場合は、検索やロックなどの処理回数を減らせます。

03.5
カーネル自身が使う領域

page allocatorとSLUB

カーネルは、プロセスの情報、ファイルの情報、ネットワークパケットなど、多数の小さなオブジェクトを作ります。物理メモリのpageをそのまま一つずつ渡すと無駄が大きいため、二段階で割り当てます。

物理メモリ

page allocator

buddy allocatorが、連続するpageを割り当てます。

同じ型のオブジェクトをまとめる

SLUB

SLUBSLUBカーネル内の小さなオブジェクトを割り当てる仕組み。はpageを小さな区画へ分け、同じ型のオブジェクトへ再利用します。

カーネルのデータ

task_struct / inode

taskinodesocket

必要な大きさに近い区画を受け取ります。