inotify(7) ファイルシステムイベントを監視する

説明

inotify API はファイルシステムイベントを監視するための機構を提供する。 inotify は個々のファイルやディレクトリを監視するのに使える。 ディレクトリを監視する場合、inotify はディレクトリ自身と ディレクトリ内のファイルのイベントを返す。

この API では以下のシステムコールが使用される。

*
inotify_init(2) は inotify インスタンスを作成し、inotify インスタンスを参照する ファイルディスクリプタを返す。 より新しい inotify_init1(2) も inotify_init(2) と同様だが、 こちらにはいくつかの追加の機能を利用するための flags 引き数がある。
*
inotify_add_watch(2) は inotify インスタンスに関連づけられた「監視対象 (watch) リスト」を操作する。 監視対象リストの各アイテム ("watch") は、 ファイルまたはディレクトリのパス名と、 そのパス名で参照されるファイルに対して カーネルが監視する複数のイベントの集合を指定する。 inotify_add_watch(2) は新しい監視アイテムの作成や既存の監視対象の変更ができる。 各監視対象は一意の「監視対象ディスクリプタ」を持つ。 これは監視対象を作成したときに inotify_add_watch(2) から返される整数である。
*
監視しているファイルやディレクトリでイベントが起こると、 それらのイベントはアプリケーションから inotify ファイルディスクリプタから read(2) を使って構造化データとして読み出すことができる (下記参照)。
*
inotify_rm_watch(2) は inotify の監視対象リストからアイテムを削除する。
*
inotify インスタンスを指している 全てのファイルディスクリプタが (close(2) を使って) クローズされた場合、 その下層にあるオブジェクトとそのリソースは、 カーネルで再利用するために解放される。 関連が切られた監視対象は自動的に解放される。

注意深くプログラミングすることで、 アプリケーションは inotify を使ってファイルシステムオブジェクトの集合の状態を効率的に監視しキャッシュしておくことができる。 しかしながら、ロバストなアプリケーションでは、監視ロジックのバグや以下に説明があるような種類の競合条件によりファイルシステムの状態とキャッシュが一致しない状態になることがあるという事実も考慮に入れておくべきである。 おそらく何らかの一貫性のチェックを行い、不一致が検出された場合にはキャッシュを再構築するのが懸命だろう。

inotify ファイルディスクリプタからのイベントの読み出し

どのようなイベントが起こっていたかを知るには、 アプリケーションで inotify ファイルディスクリプタを read(2) すればよい。 これまでに何もイベントが起こっていない場合、 停止 (blocking) モードのファイルディスクリプタであれば、 少なくとも 1 つのイベントが起こるまで read(2) は停止する (シグナルにより割り込まれなかった場合。 シグナルによる割り込みがあった場合、呼び出しはエラー EINTR で失敗する。 signal(7) 参照)。

read(2) が成功すると、以下の構造体を 1 つ以上含むバッファが返される:

struct inotify_event {
    int      wd;       /* 監視対象ディスクリプタ */
    uint32_t mask;     /* イベントのマスク */
    uint32_t cookie;   /* 関連するイベント群を関連づける
                          一意なクッキー (rename(2) 用) */
    uint32_t len;      /* 'name' フィールドのサイズ */
    char     name[];   /* ヌルで終端された任意の名前 */
};

wd はイベント発生の監視対象を指定する。 これは、前もって行われた inotify_add_watch(2) 呼び出しで返された監視対象ディスクリプタのうちの 1 つである。

mask には発生したイベント (下記参照) を記述するためのビットが含まれる。

cookie は関連するイベントを関連づけるための一意な整数である。 現在のところ、この値は rename イベントに対してのみ使われており、 結果のペアである IN_MOVED_FROMIN_MOVED_TO イベントを アプリケーションで関連づけることができる。 他のイベント種別の場合には、 cookie は 0 に設定する。

name フィールドは監視しているディレクトリ内のファイルに対して イベントが返される場合のためにだけ存在する。 監視するディレクトリからのファイルの相対パス名を表す。 このパス名はヌルで終端され、 その後の読み込みで適切なアドレス境界に調整するために、 さらにヌルバイト ('\0') が含まれる場合もある。

len フィールドはヌルバイトを含む name の全てのバイト数を表す。 よって、 inotify_event 構造体のサイズは sizeof(struct inotify_event)+len である。

read(2) に渡されたバッファが小さすぎて次のイベントに関する情報を返せ ない場合の動作はカーネルのバージョンにより異なる。 2.6.21 より前のカー ネルでは、 read(2) は 0 を返す。 2.6.21 以降のカーネルでは、 read(2) はエラー EINVAL で失敗する。 バッファサイズとして


    sizeof(struct inotify_event) + NAME_MAX + 1

を指定すれば、少なくとも 1 イベントで読み出しを行うには十分である。

inotify イベント

inotify_add_watch(2) の mask 引き数と、inotify ファイル構造体を read(2) したときに返される inotify_event 構造体の mask フィールドは、ともに inotify イベントを識別するための ビットマスクである。 以下のビットが inotify_add_watch(2) を呼ぶときの mask に指定可能であり、 read(2) で返される mask フィールドで返される:
IN_ACCESS (*)
(read(2), execve(2) などで) ファイルがアクセスされた。
IN_ATTRIB (*)
メタデータが変更された。 メタデータとは、例えば、アクセス許可 (chmod(2))、タイムスタンプ (utimensat(2) など)、拡張属性 (setxattr(2))、 リンクカウント (Linux 2.6.25 以降; link(2) のリンク先や unlink(2) など)、ユーザー/グループ ID (chown(2) など) などである。
IN_CLOSE_WRITE (*)
書き込みのためにオープンされたファイルがクローズされた。
IN_CLOSE_NOWRITE (*)
書き込み以外のためにオープンされたファイルがクローズされた。
IN_CREATE (*)
監視対象ディレクトリ内でファイルやディレクトリが作成された (open(2) O_CREAT, mkdir(2), link(2), symlink(2), UNIX ドメインソケットに対する bind(2) など)。
IN_DELETE (*)
監視対象ディレクトリ内でファイルやディレクトリが削除された。
IN_DELETE_SELF
監視対象のファイルやディレクトリ自身が削除あれた。 (このイベントはオブジェクトが別のファイルシステムに移動された場合にも発生する。 mv(1) は実際には別のファイルシステムにファイルをコピーした後、元のファイルシステムからそのファイルを削除するからである。) また、 結果的に監視ディスクリプタに対して IN_IGNORED イベントも生成される。
IN_MODIFY (*)
ファイルが変更された (write(2), truncate(2) など)。
IN_MOVE_SELF
監視対象のディレクトリまたはファイル自身が移動された。
IN_MOVED_FROM (*)
ファイル名の変更を行った際に変更前のファイル名が含まれるディレクトリに対して生成される。
IN_MOVED_TO (*)
ファイル名の変更を行った際に新しいファイル名が含まれるディレクトリに対して生成される。
IN_OPEN (*)
ファイルがオープンされた。

ディレクトリを監視する場合、 上記でアスタリスク (*) を付けたイベントは、 そのディレクトリ内のファイルに対して発生する。 このとき inotify_event 構造体で返される name フィールドは、ディレクトリ内のファイル名を表す。

IN_ALL_EVENTS マクロは上記のイベント全てのマスクとして定義される。 このマクロは inotify_add_watch(2) を呼び出すときの mask 引き数として使える。

以下の 2 つの便利なマクロが定義されている。

IN_MOVE
IN_MOVED_FROM | IN_MOVED_TO と等価。
IN_CLOSE
IN_CLOSE_WRITE | IN_CLOSE_NOWRITE と等価。

その他にも以下のビットを inotify_add_watch(2) を呼ぶときの mask に指定できる:

IN_DONT_FOLLOW (Linux 2.6.15 以降)
pathname がシンボリックリンクである場合に辿らない。 (Linux 2.6.15 以降)
IN_EXCL_UNLINK (Linux 2.6.36 以降)
デフォルトでは、あるディレクトリの子ファイルに関するイベントを監視 (watch) した際、ディレクトリからその子ファイルが削除 (unlink) された場合であってもその子ファイルに対してイベントが生成される。このことは、アプリケーションによってはあまり興味のないイベントが大量に発生することにつながる (例えば、/tmp を監視している場合、たくさんのアプリケーションが、すぐにその名前が削除される一時ファイルをそのディレクトリに作成する)。 IN_EXCL_UNLINK を指定するとこのデフォルトの動作を変更でき、監視対象のディレクトリから子ファイルが削除された後に子ファイルに関するイベントが生成されなくなる。
IN_MASK_ADD
pathname に対する監視マスクが既に存在する場合、 (マスクの置き換えではなく) イベントを追加 (OR) する。
IN_ONESHOT
1 つのイベントについて pathname を監視し、 イベントが発生したら監視対象リストから削除する。
IN_ONLYDIR (Linux 2.6.15 以降)
pathname がディレクトリの場合にのみ監視する。

以下のビットが read(2) で返される mask フィールドに設定される:

IN_IGNORED
監視対象が (inotify_rm_watch(2) により) 明示的に 削除された。もしくは (ファイルの削除、またはファイル システムのアンマウントにより) 自動的に削除された。「バグ」も参照のこと。
IN_ISDIR
このイベントの対象がディレクトリである。
IN_Q_OVERFLOW
イベントキューが溢れた (このイベントの場合、wd は -1 である)。
IN_UNMOUNT
監視対象オブジェクトを含むファイルシステムがアンマウントされた。さらに、この監視対象ディスクリプタに対して IN_IGNORED イベントが生成される。

アプリケーションがディレクトリ dir とファイル dir/myfile のすべてのイベントを監視しているとする。 以下に、これらの 2 つのオブジェクトに対して生成されるイベントの例を示す。
fd = open("dir/myfile", O_RDWR);
dirdir/myfile の両方に対して IN_OPEN イベントが生成される。
read(fd, buf, count);
dirdir/myfile の両方に対して IN_ACCESS イベントが生成される
write(fd, buf, count);
dirdir/myfile の両方に対して IN_MODIFY イベントが生成される
fchmod(fd, mode);
dirdir/myfile の両方に対して IN_ATTRIB イベントが生成される
close(fd);
dirdir/myfile の両方に対して IN_CLOSE_WRITE イベントが生成される

アプリケーションがディレクトリ dir1dir2、およびファイル dir1/myfile を監視しているとする。 以下に生成されるイベントの例を示す。

link("dir1/myfile", "dir2/new");
myfile に対して IN_ATTRIB イベントが生成され、 dir2 に対して IN_CREATE イベントが生成される。
rename("dir1/myfile", "dir2/myfile");
dir1 に対してイベント IN_MOVED_FROM が、 dir2 に対してイベント IN_MOVED_TO が、 myfile に対してイベント IN_MOVE_SELF が生成される。この際 イベント IN_MOVED_FROMIN_MOVED_TO は同じ cookie 値を持つ。

dir1/xxdir2/yy は同じファイルを参照するリンクで (他のリンクはないものとする)、 アプリケーションは dir1, dir2, dir1/xx, dir2/yy を監視しているものとする。 以下に示す順序で下記の呼び出しを実行すると、以下のイベントが生成される。

unlink("dir2/yy");
xx に対して IN_ATTRIB イベントが生成され (リンク数が変化したため)、 dir2 に対して IN_DELETE イベントが生成される。
unlink("dir1/xx");
xx に対してイベント IN_ATTRIB, IN_DELETE_SELF, IN_IGNORED が生成され、 dir1 に対して IN_DELETE イベントが生成される。

アプリケーションがディレクトリ dir と (空の) ディレクトリ dir/subdir を監視しているものとする。 以下に生成されるイベントの例を示す。

mkdir("dir/new", mode);
dir に対して IN_CREATE | IN_ISDIR イベントが生成される。
rmdir("dir/subdir");
subdir に対してイベント IN_DELETE_SELFIN_IGNORED が生成され、 dir に対して IN_DELETE | IN_ISDIR イベントが生成される。

/proc インターフェース

以下のインターフェースは、inotify で消費される カーネルメモリの総量を制限するのに使用できる:
/proc/sys/fs/inotify/max_queued_events
このファイルの値は、アプリケーションが inotify_init(2) を呼び出すときに使用され、対応する inotify インスタンスについて キューに入れられるイベントの数の上限を設定する。 この制限を超えたイベントは破棄されるが、 IN_Q_OVERFLOW イベントが常に生成される。
/proc/sys/fs/inotify/max_user_instances
1 つの実ユーザ ID に対して生成できる inotify インスタンスの数の上限を指定する。
/proc/sys/fs/inotify/max_user_watches
作成可能な監視対象の数の実 UID 単位の上限を指定する。

バージョン

inotify は 2.6.13 の Linux カーネルに組込まれた。 これに必要なライブラリのインターフェースは、 glibc のバージョン 2.4 に追加された (IN_DONT_FOLLOW, IN_MASK_ADD, IN_ONLYDIR は glibc バージョン 2.5 で追加された)。

準拠

inotify API は Linux 独自のものである。

注意

inotify ファイルディスクリプタは select(2), poll(2), epoll(7) を使って監視できる。 イベントがある場合、ファイルディスクリプタは読み込み可能と通知する。

Linux 2.6.25 以降では、シグナル駆動 (signal-driven) I/O の通知が inotify ファイルディスクリプタについて利用可能である。 fcntl(2) に書かれている (O_ASYNC フラグを設定するための) F_SETFL, F_SETOWN, F_SETSIG の議論を参照のこと。 シグナルハンドラに渡される siginfo_t 構造体は、以下のフィールドが設定される (siginfo_tsigaction(2) で説明されている)。 si_fd には inotify ファイルディスクリプタ番号が、 si_signo にはシグナル番号が、 si_code には POLL_IN が、 si_band には POLLIN が設定される。

inotify ファイルディスクリプタに対して 連続して生成される出力 inotify イベントが同一の場合 (wd, mask, cookie, name が等しい場合)、 前のイベントがまだ読み込まれていなければ、 連続するイベントが 1 つのイベントにまとめられる (ただし「バグ」の節も参照のこと)。 これによりイベントキューに必要なカーネルメモリ量が減るが、 これはまたアプリケーションがファイルイベント数を信頼性を持って数えるのに inotify を使用できないということでもある。

inotify ファイルディスクリプタの読み込みで返されるイベントは、 順序付けられたキューになる。 従って、たとえば、あるディレクトリの名前を別の名前に変更した場合、 inotify ファイルディスクリプタについての正しい順番で イベントが生成されることが保証される。

FIONREAD ioctl(2) は inotify ファイルディスクリプタから何バイト読み込めるかを返す。

制限と警告

inotify API では、inotify イベントが発生するきっかけとなったユーザやプロセスに関する情報は提供されない。とりわけ、inotify 経由でイベントを監視しているプロセスが、自分自身がきっかけとなったイベントと他のプロセスがきっかけとなったイベントを区別する簡単な手段はない。

inotify は、ファイルシステム API 経由でユーザー空間プログラムがきっかけとなったイベントだけを報告する。 結果として、 inotify はネットワークファイルシステムで発生したリモートのイベントを捉えることはできない (このようなイベントを捉えるにはアプリケーションはファイルシステムをポーリングする必要がある)。 さらに、 /proc, /sys, /dev/pts といったいくつかの疑似ファイルシステムは inotify で監視することができない。

inotify API は mmap(2) と msync(2) により起こったファイルのアクセスと変更を報告しない。

inotify API では影響が受けるファイルをファイル名で特定する。 しかしながら、アプリケーションが inotify イベントを処理する時点では、 そのファイル名がすでに削除されたり変更されたりしている可能性がある。

inotify API では監視対象ディスクリプタを通してイベントが区別される。 (必要であれば) 監視対象ディスクリプタとパス名のマッピングをキャッシュしておくのはアプリケーションの役目である。 ディレクトリの名前変更の場合、キャッシュしている複数のパス名に影響がある点に注意すること。

inotify によるディレクトリの監視は再帰的に行われない: あるディレクトリ以下の サブディレクトリを監視する場合、 監視対象を追加で作成しなければならない。 大きなディレクトリツリーの場合には、この作業にかなり時間がかかることがある。

ディレクトリツリー全体を監視していて、 そのツリー内に新しいサブディレクトリが作成されるか、 既存のディレクトリが名前が変更されそのツリー内に移動した場合、 新しいサブディレクトリに対する watch を作成するまでに、 新しいファイル (やサブディレクトリ) がそのサブディレクトリ内にすでに作成されている場合がある点に注意すること。 したがって、watch を追加した直後にサブディレクトリの内容をスキャンしたいと思う場合もあるだろう (必要ならそのサブディレクトリ内のサブディレクトリに対する watch も再帰的に追加することもあるだろう)。

イベントキューはオーバーフローする場合があることに注意すること。 この場合、イベントは失なわれる。 ロバスト性が求められるアプリケーションでは、 イベントが失なわれる可能性も含めて適切に処理を行うべきである。 例えば、アプリケーション内のキャッシュの一部分または全てを再構築する必要があるかもしれない。 (単純だが、おそらくコストがかかる方法は、 inotify ファイルディスクリプタをクローズし、 キャッシュを空にし、 新しい inotify ファイルディスクリプタを作成し、 監視しているオブジェクトの監視対象ディスクリプタとキャッシュエントリーの再作成を行う方法である。)

rename() イベントの取り扱い

上述の通り、 rename(2) により生成される IN_MOVED_FROMIN_MOVED_TO イベントの組は、共有される cookie 値によって対応を取ることができる。 しかし、対応を取る場合にはいくつか難しい点がある。

これらの 2 つのイベントは、 inotify ファイルディスクリプタから読み出しを行った場合に、通常はイベントストリーム内で連続している。 しかしながら、連続していることは保証されていない。 複数のプロセスが監視対象オブジェクトでイベントを発生させた場合、 (めったに起こらないことだが) イベント IN_MOVED_FROMIN_MOVED_TO の間に任意の数の他のイベントがはさまる可能性がある。

したがって、 rename(2) により生成された IN_MOVED_FROMIN_MOVED_TO のイベントの組の対応を取るのは本質的に難しいことである (監視対象のディレクトリの外へオブジェクトの rename が行われた場合には IN_MOVED_TO イベントは存在しさえしないことを忘れてはならない)。 (イベントは常に連続しているとの仮定を置くといった) 発見的な方法を使うと、ほとんどの場合でイベントの組をうまく見つけることができるが、 いくつかの場合に見逃すことが避けられず、 アプリケーションが IN_MOVED_FROMIN_MOVED_TO イベントが無関係だとみなしてしまう可能性がある。 結果的に、監視対象ディスクリプタが破棄され再作成された場合、これらの監視対象ディスクリプタは、処理待ちイベントの監視対象ディスクリプタと一貫性のないものになってしまう (inotify ファイルディスクリプタの再作成とキャッシュの再構成はこの状況に対処するのに有用な方法なのだが)。

また、アプリケーションは、 IN_MOVED_FROM イベントが今行った read(2) の呼び出しで返されたバッファのちょうど一番最後のイベントで、 IN_MOVED_TO イベントは次の read(2) を行わないと取得できない可能性も考慮に入れる必要がある。

バグ

2.6.16 以前のカーネルでは IN_ONESHOT mask フラグが働かない。

元々は設計/実装時の意図通り、 イベントが一つ発生し watch が削除された際に IN_ONESHOT フラグでは IN_IGNORED イベントが発生しなかった。 しかし、 別の変更での意図していなかった影響により、 Linux 2.6.36 以降では、 この場合に IN_IGNORED イベントが生成される。

カーネル 2.6.25 より前では、 連続する同一のイベントを一つにまとめることを意図したコード (古い方のイベントがまだ読み込まれていない場合に、 最新の 2 つのイベントを一つにまとめられる可能性がある) が、 最新のイベントが「最も古い」読み込まれていないイベントとまとめられるか をチェックするようになっていた。

この文書について

この man ページは Linux man-pages プロジェクトのリリース 3.65 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。