空きページの確保
1. 概要
空きメモリが少なくなってくると、ディスクキャッシュ等のページを解放して空きメモリを増やす。
2. 処理の流れ
キャッシュの解放は主にshrink_*の関数で行われる。各関数の関連は図1のようになっている。
(注) zone_reclaim()については、NUMAアーキテクチャでなければ(CONFIG_NUMA未定義)なら何もしない。よって、get_page_from_freelist()でフリーリストからページが取れなくてもzone_reclaim()によってページの回収処理は動かない。一般的なアーキテクチャではget_page_from_freelist()が失敗した後、try_to_free_pages()でページの回収が行なわれる。
3. shrink処理の開始契機
図1にもあるようにshrink処理の開始契機となるのは以下の関数。- kswapd()
- alloc_pages()
- get_page_from_freelist() (NUMAのみ)
3.1 kswapd()
空きメモリが少なくなりkswapd()がWakeupされると(kswapdはカーネルスレッド)、balance_pgdat()を呼び出してshrink処理を開始する。
3.2 alloc_pages()
ページ割当て時に割当てできなかった場合にtry_to_free_pages()を呼び出してshrink処理を開始する。
3.3 get_page_from_freelist()
CONFIG_NUMAが定義されていれば、ページ割当て(alloc_pages())でページをFreeListから取り出す時に、空きメモリ量がWaterMarkを下回っていた場合に、zone_reclaim()を呼び出してshrink処理を開始する。通常のアーキテクチャでは、ページを取得できなかった場合は何もせずリターンする。
4. 各関数の説明
4.1 shrink_cache()
指定ZoneのInactiveリストのページを幾つか(*1)解放する。
Uptodate状態のPageCacheのページなどは単純に解放されるだけだが、Dirty状態のものはディスクに書き出す必要があるのでI/Oが発生する。また、匿名ページ(Anonymous Page)は、Swapが開始される。これらの解放処理はshrink_list()を呼び出して行っている。
(*1) sc->nr_to_scanで指定された数だけInactiveリストをスキャンしてページを解放しようとする。Pageがロックされているなどして、解放できないものは飛ばされる。
4.2 shrink_list()
ページ解放処理の実作業ルーチン。引数page_listで渡されたページを解放していく。shrink_cache()からInactiveリストのページを渡されて呼び出される。ロックが取られていたり、WriteBack中のページのように処理できないものは、そのままpage_listに残して返される。
Uptodate状態のPageCacheなどは単純に解放されていくが、以下のようにページアウト処理が発生するものもある。
(1) 匿名ページでまだSwapCacheに入っていないページ
匿名ページなので、解放前にSwapパーティションに書き出す必要がある。このページは、SwapCacheに入れられて、Dirty状態になる。その後、(2)の処理によりWrite I/Oが開始される。
(2) Dirtyページ
解放前にページの内容をディスクに書き出す必要がある。pageout()を呼び出して、Write I/Oを開始する。
4.3 refill_inactive_zone()
Activeリストのページを幾つか(*2)Inactiveリストに移動する。
(*2) sc->nr_to_scanで指定された数だけInactiveリストをスキャンしてページを移動しようとする。
4.4 shrink_slab()
Active,Inactiveリストのページ以外のメモリを解放する。この関数は、shrinker_listリストに登録されたshrink用ハンドラを呼び出すだけ。
現在ハンドラとしてはinodeキャッシュなどのファイルシステム関連のメモリオブジェクトの解放ルーチンが登録されている。
4.5 shrink_zone()
指定ZoneのLRUリストのページを解放して空きメモリを確保する。refill_inactive_zone()でページをInactiveリストに移動して、shrink_cache()でページを解放させている。
優先度(sc->priority)に応じて、refill_inactive_zone(),shrink_cache()がActive/Inactiveリストをスキャンする数を決定している。
4.6 try_to_free_pages()
ページ回収処理のエントリルーチン。ページ割り当て処理で割り当て不可だった場合に呼び出される。
スキャン優先度を上げながらshrink_caches()(*3),shrink_slab()を繰りかえし呼び出してページを解放していく。優先度が上がるとshrink_zone()以下でスキャンするページ数が多くなるのでよりたくさんのページが回収されやすくなる。
SWAP_CLUSTER_MAXだけページを回収できたら1を返す。
4.7 zone_reclaim()
指定Zoneから指定ページ数(2^nで指定)を回収する。CONFIG_NUMA未定義時は空の関数(return 0)となる。
scan_controlの優先度を上げながらshrink_zone()を呼び出して、ページを解放させていく。
shrink_zone()でも必要ページ数を回収できなかった場合は、shrink_slab()でさらにページを回収しようとする。
4.8 balance_pgdat()
kswapdから呼び出されるページ回収ルーチン。
引数nr_pagesで解放するページを指定する。0が指定されたら各Zoneの空きページ数がWaterMark(High)を上まわるまでページを回収しようとする。kswapdからの呼び出し時は0。
指定ページ数を回収するまで、スキャン優先度を上げながらshrink_zone(),shrink_slab()を繰りかえし呼び出してページを回収していく。