Linuxでファイルにアクセスするたびに毎回ディスクを読みに行っていたら、処理はとてつもなく遅くなります。
これを防ぐために使われているのが「inodeキャッシュ」です。
この記事では、inodeキャッシュの役割・仕組み・確認方法・チューニング手順をわかりやすく解説します。
inodeとは
inodeキャッシュを理解するには、まずinode(アイノード)の概念を押さえる必要があります。
inodeとは、LinuxのVFS(仮想ファイルシステム)層が管理する構造体で、ファイルやディレクトリのメタデータを保持するものです。
具体的には次のような情報を格納しています。
- ファイルの種類・アクセス権限(パーミッション)
- 所有者のUID・GID
- ファイルサイズ
- タイムスタンプ(アクセス日時・更新日時・変更日時)
- ディスク上のデータブロックの位置
- ハードリンクの数
注意点として、ファイル名はinodeには含まれません。
ファイル名を管理するのは後述する「dentry」の役割です。
statコマンドで特定のファイルのinode情報を確認できます。
stat .bashrc
# 出力例:
# Inode: 29984943 Links: 1
# Access: (0644/-rw-r--r--)
# Modify: 2017-06-16 10:29:16
inodeキャッシュとdentryキャッシュ
Linuxカーネルは、ファイルアクセスを高速化するためにinodeキャッシュとdentryキャッシュという2種類のキャッシュをメモリ上に保持します。
セットで語られることが多いため、両者の役割の違いを整理します。
inodeキャッシュ
ディスクから読み込んだinodeの内容(メタデータ)をメモリ上に保持するキャッシュです。
カーネルソースでは inode_cachep というSlab(スラブ)キャッシュとして実装されており、ファイルシステムごとに専用のSlab(ext4_inode_cacheなど)が用意される場合もあります。
dentryキャッシュ
dentry(ディレクトリエントリ)は、ファイル名・ディレクトリ名とinodeを結びつける構造体です。
また、ディレクトリの親子関係(階層構造)もdentryが保持しています。
たとえば /home/user/file.txt というパスにアクセスするとき、カーネルは/→home→user→file.txtと順にdentryをたどります。
この解決処理(パス解決)は繰り返すと重いため、一度たどったdentryはdentryキャッシュ(dentry_cache)に保持されます。
| 比較項目 | inodeキャッシュ | dentryキャッシュ |
|---|---|---|
| 保持する情報 | ファイルのメタデータ | ファイル名・ディレクトリ階層 |
| 対応する構造体 | struct inode | struct dentry |
| Slab名 | inode_cache(fsごとに異なる) | dentry |
| 親子関係 | なし | あり |
キャッシュが生成されるタイミング
inodeキャッシュとdentryキャッシュは、パス解決(特定のパスに対応するファイルを探す処理)の際に生成されます。open()やstat()などのシステムコールが呼ばれると、カーネルはパスを先頭から順にたどり、対応するdentryとinodeをメモリ上に構築してキャッシュします。
これにより2回目以降の同一パスへのアクセスはディスクI/Oなしに完了します。
ネガティブキャッシュ
存在しないファイルに対して open() が呼ばれた場合、カーネルは「このパスは存在しない」という情報もネガティブキャッシュとしてdentryに保持します。
これにより、存在しないファイルへの繰り返しアクセスでも、毎回ディスクを探しに行かずにすぐ ENOENT を返せます。
キャッシュの効果
キャッシュがどれほど有効かを示す例として、キャッシュありとなしで同一のファイルツリーをスキャンした場合の時間差が確認されています。
# キャッシュをクリアしてからスキャン
echo 3 > /proc/sys/vm/drop_caches
time ls -R / > /dev/null
# real 0m9.140s
# キャッシュに乗った状態でスキャン
time ls -R / > /dev/null
# real 0m0.747s
2回目の実行が約1/12の時間で完了していることがわかります。
この差がinodeキャッシュ・dentryキャッシュの効果です。
キャッシュの確認方法
slabtopコマンド
Slabキャッシュの使用状況をリアルタイムで確認できます。
sudo slabtop
出力の dentry・inode_cache・ext4_inode_cache などの行がそれぞれのキャッシュサイズに対応します。
/proc/slabinfo
より詳細なSlab情報はファイルから確認できます。
cat /proc/slabinfo | grep -E "dentry|inode"
/proc/meminfo
メモリ全体の概要から、回収可能なSlabメモリ(inodeキャッシュ・dentryキャッシュを含む)の量を確認できます。
grep SReclaimable /proc/meminfo
SReclaimable の値が、メモリ不足時に回収可能なSlabメモリ(inodeキャッシュ・dentryキャッシュなどを含む)の総量です。
キャッシュの管理・チューニング
自動解放
Linuxカーネルは、メモリが必要になると自動的にinodeキャッシュ・dentryキャッシュを解放します。
Linuxカーネルの公式ドキュメントには、「これらのオブジェクトは、システムの他の部分でメモリが必要になると自動的に回収される」と明記されています(kernel.org「Documentation for /proc/sys/vm/」)。
通常運用では手動での介入は不要です。
drop_cachesによる手動クリア
テストやデバッグ目的で手動クリアする場合は、まず sync を実行してから drop_caches に値を書き込みます。
sync
echo 2 > /proc/sys/vm/drop_caches # dentryとinodeのキャッシュをクリア
echo 3 > /proc/sys/vm/drop_caches # ページキャッシュも含めてすべてクリア
| 値 | 対象 |
|---|---|
| 1 | ページキャッシュのみ |
| 2 | dentryキャッシュとinodeキャッシュ |
| 3 | ページキャッシュ・dentryキャッシュ・inodeキャッシュのすべて |
注意: カーネル公式ドキュメントは、drop_caches の使用は「テスト・デバッグ環境以外では推奨しない」と明記しています。
キャッシュを捨てると再生成のためのI/OとCPUコストが発生するためです(kernel.org「Documentation for /proc/sys/vm/」)。
vfs_cache_pressureによるチューニング
vm.vfs_cache_pressure は、カーネルがinodeキャッシュ・dentryキャッシュを回収する積極性を制御するパラメータです。
# 現在の値を確認
cat /proc/sys/vm/vfs_cache_pressure
# 一時的に変更(再起動で元に戻る)
sysctl -w vm.vfs_cache_pressure=200
# 永続的に変更する場合は /etc/sysctl.conf に追記
vm.vfs_cache_pressure = 200
| 値 | 挙動 |
|---|---|
| デフォルト(100) | ページキャッシュと公平な比率でdentry・inodeキャッシュを回収 |
| 100未満 | dentry・inodeキャッシュを優先して保持(解放を抑える) |
| 100超 | dentry・inodeキャッシュをより積極的に回収する |
vfs_cache_pressure を大きくしすぎるとパフォーマンスに悪影響が出る場合があります(kernel.org公式ドキュメント)。
変更する場合は段階的に試し、slabtop で効果を確認しながら調整してください。
inodeキャッシュが大量に消費されるケース
通常はカーネルが自動管理しますが、特定の状況ではキャッシュが急増することがあります。
- 大量の一時ファイルを生成・削除するアプリケーション
ファイルを削除してもdentry・inodeのキャッシュはすぐには解放されないため、メモリ使用量が増え続ける場合があります。 - 深いディレクトリ階層へのアクセスが多い
階層が深いほどパス解決で生成されるdentryの数が増えます。 - 大量のユニーク名ファイルが蓄積されるディレクトリ
各ファイルに対してinode・dentryが個別に生成されます。
このようなケースでは、tmpfs(/dev/shm など)を一時ファイルの置き場にすることで、dentryキャッシュの蓄積を回避できることが知られています。
まとめ
inodeキャッシュ(およびdentryキャッシュ)は、Linuxカーネルがファイルアクセスを高速化するためにメモリ上に保持するVFS層のキャッシュです。
通常はカーネルが自動的に管理・解放するため、特別な操作は不要です。
大量の一時ファイル生成などの特殊なワークロードでキャッシュが肥大化する場合は、vfs_cache_pressure の調整や tmpfs の活用を検討してみてください。
参考情報源:

コメント