Linuxカーネルのページキャッシュについて調べてみた

概要

詳解Linuxカーネル読書会に参加してlinuxカーネル4.4.0のページキャッシュについて、本とソースコードを読みながら調べてみた。

ページキャッシュとは

I/Oデータのキャッシュを行うためにLinuxカーネルが用意しているメモリのことである。例えばディスクにwriteする前は一旦ページキャッシュにデータが書かれたあと、しばらくしてまとめてページキャッシュがディスクに転送される。Readの場合も同様に一旦ページキャッシュに載せられたデータがユーザーに返される。

アドレス空間オブジェクト

ページキャッシュの核となるaddress_space構造体

アドレス空間オブジェクトの初期化

アドレス空間オブジェクトの初期化は各ファイルシステムの実装でinodeキャッシュを作るときにinit_once関数をkmem_cache_createのコールバック関数として指定して初期化される。
ext4の場合はext4/super.cのinit_inodecache関数でそれが行われている。

ext4のinit_onceは以下のとおり。リストとセマフォの初期化をした後にinode_init_once関数を呼ぶ。

inode_inot_once関数はfs/inode.cで定義されている。この関数ではinodeのメモリ初期化とリストの初期化などを行っている。address_space_init_once関数でinode->i_dataをアドレススペース構造体として初期化している。

address_space_init_once構造体では、アドレススペースのメモリ初期化と構造体の基数ツリー(radix tree)の初期化や、その他のリストの初期化、セマフォの初期化などを行っている。i_mmapフィールドは優先検索用基数ツリーらしい。i_mmapの詳細は詳解Linuxの17章を参照とのこと。

アドレススペースオブジェクトに対する操作

アドレススペースオブジェクトに対する操作はa_opsフィールドのaddress_space_operations構造体に格納されてる各関数ポインタによって行われる。

基数ツリー(Radix Tree)

基数ツリーとはページキャッシュを線形検索しなくてもすむように、アドレススペースオブジェクトに1つずつ用意された検索ツリーである。基数ツリーはradix_tree_rootから始まり、1つ以上のradix_tree_nodeを持つ。radix_tree_nodeは64個のスロットを持ち、各スロットは、ページディスクリプタへのポインタか、radix_tree_nodeへのポインタを持つ。ツリーの階層数は最大で6であり、この時のファイルサイズは16TBになる。

基数ツリーとは:Wikipediaより

ページキャッシュの操作関数

ページ検索

ページ検索はfind_get_page関数で行われる。find_get_page関数の実体はpagecache_get_page関数である。まずfind_get_entry関数で、既存のページエントリーが存在するかチェックする。存在すれば、そのページエントリーをリターンして終了。存在しなければ__page_cache_alloc関数でページディスクリプタをallocしてadd_to_page_cache_lru関数でページディスクリプタををページキャッシュに追加する。

find_get_entry関数は以下のようになっている。検索の実態はradix_tree_lookup_slotになっている。ただrcu_read_lockを取っているのにページキャッシュのロックレスの処理をしているのはなぜなのか?要調査。

radix_tree_deref_slotの実態が__radix_tree_lookupでradex treeのrootからindexの葉の検索をする関数っぽい。

ページ検索が行われる代表としてfile readの処理でどのようにfind_get_page関数が使われるか見てみる。filepからアドレススペースオブジェクトを取得して、アクセスするファイルの位置pposからindex(何番目のページか)を取得してfind_get_pageに渡している。このdo_generic_file_read関数はgeneric_file_read_iterから呼びだされる。generic_file_read_iter関数はページキャッシュから直接データを読み込むext4などのすべてのファイルシステムで使われている。

ページの追加

ページの追加はadd_to_page_cache_lruで行われる。add_to_page_cache_lruの実態は__add_to_page_cache_locked

基数ツリーに実際に挿入してるのはpage_cache_tree_insert関数のようである。この関数では、まず__radix_tree_createで基数ツリーにスロットを作成する。その後、radix_tree_replace_slot関数でslotにページディスクリプタをセットする。

ページの削除

ページの削除はdelete_from_page_cache関数で行われる。
詳しいことはまた次回!

ページの更新

ページの更新はread_cache_pages関数で行われると思われる。
詳しいことはまた次回!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です