inotify/fanotifyによるファイルシステム監視
ファイル変更をポーリングなしで検知する仕組みを原理から解説。inotifyのwatch管理とイベントキュー、fanotifyの権限イベントによるアクセス遮断、そして大規模監視で詰まる限界までつかめます。
- 1.inotifyは監視対象ごとにwatch記述子(wd)を割り当て、イベントを単一のfdのキューにためる。fdはread可能になるためepollに統合でき、再帰監視やキュー溢れ(IN_Q_OVERFLOW)の扱いが実装の肝。
- 2.fanotifyはVFSにフックして開く前にプロセスを止め、許可/拒否を返す権限イベント(FAN_OPEN_PERM等)を持つ。マウントやファイルシステム単位で一括監視でき、アンチウイルスやアクセス制御に向く。
- 3.ディレクトリツリー全体の監視はinotifyではディレクトリ数に比例するwatch登録が必要で、メモリ上限とイベント取りこぼしが構造的限界。fanotifyのFAN_MARK_FILESYSTEMがこの再帰問題を回避する。
なぜカーネルにファイル監視機構が要るのか
「あるファイルが変更されたら再ロードする」「ディレクトリにファイルが置かれたら処理する」——この要求をユーザー空間だけで満たすなら、定期的に stat を叩いて更新時刻を比べるポーリングしかありません。これは監視対象が増えるほど無駄なシステムコールが線形に増え、検知も間隔ぶんだけ遅れます。そこでカーネルが「変化が起きた瞬間に教える」イベント通知機構を提供します。本記事は Linux の dnotify → inotify → fanotify という進化を、それぞれが何の限界を解こうとしたかという視点で内部から解剖します。土台にあるのは VFS(仮想ファイルシステム)層 で、ファイル操作はすべてここを通るため、監視フックを仕込む自然な場所になります。
dnotify:最初の試みとその限界
歴史的に最初の機構が dnotify(Linux 2.4)でした。これはディレクトリにシグナルを結びつける設計です。fcntl(fd, F_NOTIFY, ...) で開いたディレクトリfdに対し、変更が起きると SIGIO(シグナル 参照)が飛ぶ、という方式です。しかしこの設計には実用上致命的な弱点がありました。
- 監視対象ごとにファイルディスクリプタが要る:ディレクトリを開きっぱなしにするため、fdを大量に消費する。
- マウントを妨げる:開いているとそのファイルシステムを
umountできない。 - シグナルの非同期性:どのファイルが変わったかがシグナルだけでは分からず、結局
statで差分を取り直す必要があり、競合も起きやすい。 - ディレクトリ単位:個々のファイルを名指しで監視できない。
これらを根本から作り直したのが inotify です。
inotify:watchとイベントキューの構造
inotify(Linux 2.6.13)の核心は、監視状態を1本のファイルディスクリプタに集約することです。APIは小さく3段で構成されます。
int ifd = inotify_init1(IN_NONBLOCK); // 監視インスタンス(fd)を生成
int wd = inotify_add_watch(ifd, "/path", IN_MODIFY|IN_CREATE); // 監視対象を登録 → wdが返る
read(ifd, buf, sizeof(buf)); // 溜まったイベントを読み出す
inotify_add_watch は監視対象(ファイルまたはディレクトリ)ごとに watch記述子(wd, watch descriptor) という整数を返します。これは内部でインスタンスにぶら下がる watch オブジェクトを指す一意なIDです。同じパスを再登録するとマスクが置き換えられ、wdは変わりません(新規fdは消費しない)。
監視対象に変化が起きると、カーネルは struct inotify_event をインスタンスのイベントキューに積みます。アプリは ifd を read するだけで、溜まった可変長イベントを順に取り出せます。各イベントは「どのwdか・何が起きたか(mask)・名前」を持ちます。
inotify_event のレイアウト(可変長):
┌─────┬────────┬────────┬─────┬──────────────┐
│ wd │ mask │ cookie │ len │ name[len] │ ← ディレクトリ配下のエントリ名
└─────┴────────┴────────┴─────┴──────────────┘
どの監視 何が起きたか rename対応 名前長
決定的に重要なのは、ifd が通常のfdとして read 可能・poll 可能な点です。これにより inotify は epoll のイベントループへそのまま組み込めます。「ファイル変化」と「ネットワークI/O」を同じ多重化ループで一元的に待てるわけで、これがシグナルベースの dnotify との決定的な使い勝手の差です。fd周りの管理構造は ファイルディスクリプタテーブル のとおりです。
ファイル移動は IN_MOVED_FROM(移動元)と IN_MOVED_TO(移動先)の2イベントに分かれます。両者は同じ cookie 値を持つので、これを突き合わせて初めて「AがBへrenameされた」と再構成できます。別々に届くため、片方だけ処理すると移動を取りこぼします。
inotify の構造的な限界
inotify は優れていますが、設計上の限界がはっきりしています。これは試験・実務の両方で問われる勘所です。
第一に、監視は再帰しません。ディレクトリを登録しても、その「直下のエントリ」の変化は分かりますが、サブディレクトリ内部は別途登録が要ります。よってツリー全体を監視するには、全ディレクトリを個別に walk して add_watch する必要があり、watch数は概ねディレクトリ数に比例します。
第二に、watch数には上限があります。/proc/sys/fs/inotify/max_user_watches で、巨大なソースツリーを監視するエディタやビルドツールがこの上限に当たって ENOSPC を返す、という障害は頻出です。各watchはカーネルメモリ(64bit機で1件あたり数百バイト程度)を消費するため、無制限にはできません。
第三に、イベントキューは有限で、消費が追いつかないと溢れます。
キューが max_queued_events を超えると、カーネルは以降のイベントを捨て、代わりに IN_Q_OVERFLOW(wd = -1)を1つだけ積みます。これを受けたら「いくつか取りこぼした」ことしか分からないため、堅牢な実装は対象を再スキャンして状態を取り直すフォールバックを必ず持ちます。大量ファイルを一括展開した直後などに起きやすい点に注意。
第四に、登録と最初の read の間に起きた変化は捕捉できず、TOCTOU 的な競合が残ります。「監視を張ってから初期スキャンする」順序を守らないと、その隙間の変更を落とします。
fanotify:権限イベントとマウント単位監視
fanotify(Linux 2.6.37〜)は inotify とは別系統で、inotify が苦手な領域を埋めるために作られました。違いは大きく2点です。
ひとつは監視の粒度。fanotify は個々のディレクトリではなく、FAN_MARK_MOUNT でマウント単位、FAN_MARK_FILESYSTEM(Linux 4.20+)でファイルシステム全体をまとめて監視できます。これは再帰問題を一掃します。マウント1点にマークすれば、その配下で起きるアクセスをツリーを walk せずにすべて拾えるからです。
もうひとつ、そして本質的なのが権限イベント(permission event)です。inotify が「起きたこと」を事後に伝える通知専門なのに対し、fanotify は操作を実行する前にプロセスを一時停止させ、監視側の判断を待つことができます。
fanotify_mark(ffd, FAN_MARK_ADD | FAN_MARK_MOUNT,
FAN_OPEN_PERM, // 「開く」操作の権限イベント
AT_FDCWD, "/mnt/data");
// イベント受信ループ
read(ffd, &meta, sizeof(meta)); // 対象プロセスはここでブロックされている
struct fanotify_response resp = { .fd = meta.fd, .response = FAN_ALLOW }; // or FAN_DENY
write(ffd, &resp, sizeof(resp)); // 許可/拒否を返して初めて相手が動く
FAN_OPEN_PERM や FAN_ACCESS_PERM を登録すると、対象プロセスが open/read を呼んだ瞬間にカーネル内で停止し、監視側が FAN_ALLOW か FAN_DENY を write するまで待たされます。これにより、アンチウイルスが「スキャンしてからでないと開かせない」、階層型ストレージ管理(HSM)が「アクセスされたら裏でファイル実体を呼び戻す」といった割り込み型のアクセス制御が実装できます。アクセス判断にフックする発想自体は LSM(Linux Security Module) と同系統です。
権限イベントの監視プロセスが応答を返さないと、対象は無期限に止まります。さらに監視プロセス自身が監視対象のファイルシステムにアクセスすると、自分の open が自分のキューにイベントを積み、応答できずに固まる自己デッドロックが起きえます。受け取った event.fd の close 忘れによるfdリークも定番の事故です。
fanotify は受信時に「どのファイルか」を開かれたfdとして渡してくる(古いモード)ため、監視側がパス解決の競合に悩まされにくい反面、fdを消費します。Linux 5.1 以降の FAN_REPORT_FID モードでは、fdの代わりに**ファイルハンドル(fid)**を渡せるようになり、さらに Linux 5.9 以降の FAN_REPORT_DFID_NAME で FAN_CREATE/FAN_DELETE などディレクトリエントリ変更(親ディレクトリのfidとエントリ名)も拾えるようになって、inotify の用途にも踏み込んでいます。
inotify と fanotify の使い分け
| 観点 | inotify | fanotify |
|---|---|---|
| 監視の粒度 | ファイル/ディレクトリ単位(再帰なし) | マウント単位・ファイルシステム単位が可能 |
| 大規模ツリー | 全ディレクトリにwatch登録が必要 | マークひとつで配下を一括監視 |
| 権限イベント | なし(事後通知のみ) | あり(開く前に許可/拒否を返せる) |
| イベントの中身 | wd+mask+名前 | fd または fid+mask(モード依存) |
| fd消費 | インスタンス1つ+watchはfd不要 | 対象ごとにfdを受け取る場合あり |
| 権限 | 一般ユーザー可 | CAP_SYS_ADMIN(多くの機能で必須) |
| 主な用途 | 設定リロード・ビルド監視・同期 | アンチウイルス・HSM・アクセス監査 |
実務では事後の変更検知なら inotify、アクセスを止めて判断したい/巨大ツリーを丸ごと見たいなら fanotify と分けるのが基本です。なお fanotify の多くの機能は CAP_SYS_ADMIN を要し、誰でも使える inotify とは前提が異なります。
「inotifyとfanotifyの最大の違いは」と問われたら、fanotifyは権限イベントで操作を事前に遮断でき、マウント/ファイルシステム単位で監視できること、inotifyは再帰せず事後通知のみであること、を挙げれば芯を捉えています。さらに「inotifyで大きなツリーを監視する際の限界」は、watch数の上限(max_user_watches)とIN_Q_OVERFLOWによる取りこぼしが定番の答えです。
スケーラビリティの限界をどう越えるか
両機構に共通する本質は、「カーネルがイベントを生成し、ユーザー空間がそれを消費する」生産者・消費者モデルだという点です。したがって消費が生産に追いつけば溢れるのは避けられません。実装で押さえるべき指針は次の通りです。
- 溢れ前提で設計する:
IN_Q_OVERFLOWや fanotify のFAN_Q_OVERFLOWを受けたら、差分通知を信じきらず再スキャンで真の状態を取り直す経路を必ず用意する。 - 再帰監視のコストを見積もる:inotify でツリーを監視するなら watch 数 ≒ ディレクトリ数。数十万規模なら
max_user_watchesの調整か、FAN_MARK_FILESYSTEMへの切り替えを検討する。 - イベントを素早く吸い出す:
readで一度に複数イベントをまとめて取り、重い処理は別ワーカーへ渡してキュー滞留を防ぐ。epoll でノンブロッキングに回すのが定石。 - 監視自身の副作用を避ける:fanotify の権限イベントでは、監視プロセスが対象ファイルシステムへ触れない(または
FAN_MARK_IGNORED_MASKで自分を除外する)よう設計し、自己デッドロックを断つ。
まとめ
進化の軸は「ポーリングの無駄を消し、必要なら操作を事前に止める」へ向かいます。dnotifyはディレクトリにシグナルを結ぶ素朴な方式で、fd浪費・umount阻害・粒度の粗さに行き詰まりました。inotifyは監視状態を1本のfdに集約し、watch記述子とイベントキューで「変化したものだけ」を read/epoll で受け取れるようにした一方、再帰しない・watch数とキューに上限があるという構造的限界を抱えます。fanotifyはVFSにフックしてマウント/ファイルシステム単位で監視し、権限イベントで開く前に許可・拒否を返せる点が決定的で、アンチウイルスやアクセス制御の土台になります。いずれも生産者・消費者モデルゆえ溢れたら再スキャンという安全弁が不可欠——ここが腑に落ちると、ファイル監視の堅牢な設計が一本の線でつながります。
OS Article
inotify/fanotifyによるファイルシステム監視を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
inotify
比較で見る軸
難易度: advanced / カテゴリ: OS / タグ数: 6
導入後に効く点
fanotifyはVFSにフックして開く前にプロセスを止め、許可/拒否を返す権限イベント(FAN_OPEN_PERM等)を持つ。マウントやファイルシステム単位で一括監視でき、アンチウイルスやアクセス制御に向く。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- OS
- タグ数
- 6
判断チェックリスト
- 自社の用途が「inotify / fanotify」に近いか確認する。
- 強みである「inotifyは監視対象ごとにwatch記述子(wd)を割り当て、イベントを単一のfdのキューにためる。fdはread可能になるためepollに統合でき、再帰監視やキュー溢れ(IN_Q_OVERFLOW)の扱いが実装の肝。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。