Linux Kernel 2.6 Documentation:
/usr/src/linux/Documentation/filesystems/dnotify.txt
filesystems/dnotify.txt
Linux ディレクトリ通知機能 (ディレクトリ内で発生したイベントを受け取る仕組み)
[プレインテキスト版]
- 原著作者: Stephen Rothwell <sfr@canb.auug.org.au>
- 翻訳者: 川崎 貴彦 <takahiko(a)hakubi.co.jp>
- バージョン: 2.6.3
- 翻訳日時: 2004/02/08
Linux ディレクトリ通知機能
==========================
Stephen Rothwell <sfr@canb.auug.org.au>
ディレクトリ通知機能の目的は、ディレクトリ自体、もしくはその内部の
ファイルに変更がかかったときに、ユーザアプリケーションにそのことを
通知することにあります。基本的なメカニズムは、ユーザアプリケーション
から fcntl(2) コールを用いてディレクトリに対する通知登録をおこなって
もらい、通知自体はシグナル送信でおこなう、というものです。
アプリケーションでは、どのイベントが発生したときに通知を受け取るのかを
決めることができます。現在定義されているイベントは次のとおりです。
DN_ACCESS ディレクトリ内のファイルがアクセスされた (read)
DN_MODIFY ディレクトリ内のファイルが変更された (write,truncate)
DN_CREATE ディレクトリ内にファイルが作成された
DN_DELETE ディレクトリ内からファイルが削除された
DN_RENAME ディレクトリ内のファイルの名前が変更された
DN_ATTRIB ディレクトリ内のファイルの属性が変更された (chmod,chown)
通常は、通知を受けるたびにアプリケーション側で再度登録をしなければ
なりません。しかし、イベントマスクが DN_MULTISHOT との論理和として
与えられている場合は、あえて登録を抹消しない限り有効のままとなります
(「イベント無し」を登録することで抹消とします) 。
デフォルトでは、プロセスに対して SIGIO シグナルが送信されるだけであり、
その他の有益な情報は得られません。しかし、F_SETSIG fcntl(2) を使って
送信するシグナルをカーネルに伝えておけば、シグナルハンドラに siginfo
構造体が渡されます。この構造体のメンバ変数である si_fd には、イベントが
発生したディレクトリに対応するファイル記述子が入っています。
アプリケーションでリアルタイムシグナル (SIGRTMIN + <n>) のいずれか
一つを使用することをお勧めします。そうすれば、通知をキューイングする
ことができます。特に、DN_MULTISHOT を指定する場合はこれが肝心です。
SIGRTMIN はたいていブロックされているので、(少なくとも) SIGRTMIN + 1
を使うほうが良いということに注意してください。
実装の見込み (機能もバグも :-))
------------
ファイルに対してローカルマシンからのアクセスがあったときは、全て通知される
はずです。ファイルの実体が実際にはリモートサーバ上に存在するという場合でも
同様です。これは、ローカルマシン上で動作するユーザモードのデーモンやカーネル
モードの NFS サーバを経由して、リモートからアクセスがあった場合にも同様に
通知されることを意味しています。
ファイルシステムのコードに対する変更を最小限に抑えるため、ハードリンクに
関する問題は無視しています。ですので、ファイル x が二つのディレクトリ
(a と b) に存在しているとき、"a/x" という名前のファイルに対して変更が
かかった場合、ディレクトリ "a" 上で通知を待っているプログラムに対しては
通知がおこなわれますが、ディレクトリ "b" 上で通知を待っているプログラム
に対しては通知はおこなわれません。
また、ファイルが削除 (unlink) されても、そのファイルが最後に存在して
いた (link されていた) ディレクトリで通知が発生します。
例
--
#define _GNU_SOURCE /* 記号定数の定義を得るために必要 */
#include <fcntl.h> /* glibc 2.2 では、ここに必要な値が定義されている */
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static volatile int event_fd;
static void handler(int sig, siginfo_t *si, void *data)
{
event_fd = si->si_fd;
}
int main(void)
{
struct sigaction act;
int fd;
act.sa_sigaction = handler;
sigemptyset(&act.sa_mask);
act.sa_flags = SA_SIGINFO;
sigaction(SIGRTMIN + 1, &act, NULL);
fd = open(".", O_RDONLY);
fcntl(fd, F_SETSIG, SIGRTMIN + 1);
fcntl(fd, F_NOTIFY, DN_MODIFY|DN_CREATE|DN_MULTISHOT);
/* "." 内のファイルが修正されるか、または新しい
ファイルが作成されれば、通知を受け取る。 */
while (1) {
pause();
printf("ファイル記述子 %d 上でイベントが発生しました\n", event_fd);
}
}
------------------------------------------------------------
翻訳団体: JF プロジェクト < http://www.linux.or.jp/JF/ >
翻訳者: 川崎 貴彦 < takahiko(a)hakubi.co.jp >
翻訳日: 2004/02/08
校正者: JF プロジェクトと Linux Kernel Hack Japan
< http://lkh.linux.or.jp/ > の方々(メール到着順)
野本 浩一 さん < hng(a)ps.ksky.ne.jp >
花高 信哉 さん < hanataka(a)abyss.rim.or.jp >
塚本 明 さん < akira-t(a)mb.kcom.ne.jp >
大久保 克彦 さん < ohkubo-k(a)suri.co.jp >
高橋 浩和 さん < taka(a)valinux.co.jp >
草野 貴之 さん < AE5T-KSN(a)asahi-net.or.jp >
佐野 武俊 さん < kgh12351(a)nifty.ne.jp >
高橋 聡 さん < hisai(a)din.or.jp >
芳賀 靖史 さん < yasufumi.haga(a)nifty.com >
武井 伸光 さん < VEC05526(a)nifty.ne.jp >
山下 義之 さん < dica(a)eurus.dti.ne.jp >
Seiji Kaneko さん < skaneko(a)a2.mbn.or.jp >
=== 以下、訳者による追加 ===================================
翻訳中、原文の最後の文 "Also, files that are unlinked, will still cause
notifications in the last directory that they were linked to." の意味する
ことが分からず、Linux Kernel Hack Japan < http://lkh.linux.or.jp/ > メー
リングリストに質問させていただいたところ、疑問が解決しました。下記は、
その際に得られた情報をまとめたものです。
最後の文の要点は、次のとおりです。
--------------------------------------------------
プロセスAがファイルXをオープン後、プロセスBが
ファイルXを削除してしまったとしても、プロセスA
から見ると、プロセスA自身がファイルXをクローズ
するまでは、ファイルXは存在しているように見える。
プロセスBがファイルXを削除したあとからプロセス
AがファイルXをクローズするまでの間に、プロセス
AがファイルXに対して何らかの DN_* イベントを
発生させる処理をおこなった場合、そのイベントは
通知される。
--------------------------------------------------
次の作業をおこなえば、この動作を確認することができます。
(1) 上記のプログラムを DN_ACCESS イベントだけを監視するように修正して
コンパイルし、watcher というプログラム名をつける。
(2) ファイルをオープン、リード、クローズするだけの下記のプログラムを
新規作成してコンパイルし、glancer というプログラム名をつける。
---------------------------------------------------------
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int main(int argc, char **argv)
{
int fd;
char buf[BUFSIZ];
/*
* 第一引数で指定されたファイルをリードオンリーで
* オープンし、何かキー入力があるまで待機する。
*/
if ( (fd=open(argv[1],O_RDONLY)) == -1 ) return -1;
getchar();
/*
* 何かキー入力があったら、ファイルの内容を読み出す。
* この read により、DN_ACCESS イベントが発生する。
* 上記 open と下記 read の間に他のプロセスが当該
* ファイルを削除しても、DN_ACCESS は通知される。
*/
read(fd, buf, sizeof(buf));
close(fd);
return 0;
}
---------------------------------------------------------
(3) "mkdir dir; echo xxx > dir/xxx" でファイル dir/xxx を作成する。
(注:単なる "touch dir/xxx" では空のファイルが作成されるだけ
なので、glancer の read() で DN_ACCESS イベントが発生しない)
(4) dir ディレクトリに移動し、watcher を起動する。
(5) 他のターミナルで、"glancer dir/xxx" で glancer を起動する。glancer は
dir/xxx をオープン後、キー入力待ちになる。
(6) glancer を待機状態にさせたまま、他のターミナルから "rm dir/xxx" を
実行して dir/xxx を削除する。削除後、"ls dir" とすると、当然ながら
dir/xxx が存在しないことがわかる。
(7) 待機状態になっている glancer に対して、何かキー入力をおこなう。
すると、glancer は dir/xxx の内容をリードする。このリードにより
DN_ACCESS イベントが発生する。この DN_ACCESS は、(4) で起動された
watcher に通知される。
(8) glancer 終了後、"ls dir" としても、dir/xxx は存在しない。
Linux カーネル 2.6 付属文書一覧へ戻る