Linuxのinotify機能と実装原理の詳細説明

PHPz
リリース: 2024-02-13 19:45:30
転載
1032 人が閲覧しました

详解Linux inotify功能及实现原理

1. inotify の主な機能

MAC や Windows と比較すると、Linux デスクトップ システムはいくつかの点でまだ改善の必要があります。この状況を改善するために、オープンソース コミュニティは、ユーザー モードがカーネルまたは基礎となるハードウェア デバイスの変更をタイムリーに学習して、デバイスをより適切に管理し、より良いサービスを提供できるようにするいくつかのメカニズムを提案しました。その中でも、ホットプラグは、カーネルがホットプラグ デバイスに関するイベントをユーザー モード アプリケーションに通知するメカニズムであり、デスクトップ システムがデバイスを効果的に管理するのに役立ちます。 udev は /dev の下にデバイス ファイルを動的に維持しますが、inotify はファイルの追加、削除、その他のイベントなどのユーザー モードのファイル システムの変更を即座に通知できるファイル システム変更通知メカニズムです。このメカニズムはもともと有名なによって使用されていました。デスクトップ検索エンジン プロジェクト beagle によって使用され、Gamin などのプロジェクトで広く使用されています。 inotify は、ファイル システムの変更をユーザー空間プログラムに通知するために使用されるカーネル メカニズムであることに注意してください。

2. ユーザー インターフェース

ユーザー モードでは、inotify は 3 つのシステム コールと、返されたファイル記述子に対するファイル I/操作を通じて使用されます。inotify を使用する最初のステップは、inotify インスタンスを作成することです:

リーリー

各 inotify インスタンスは、独立したソート キューに対応します。

ファイル システムの変更イベントは、ウォッチと呼ばれるオブジェクト管理です。各ウォッチはタプル (ターゲット、イベント マスク) です。ターゲットはファイルまたはディレクトリにすることができます。イベント マスクは、アプリケーションが必要とする inotify イベントを表しますに注意してください。各ビットは inotify イベントに対応します。監視オブジェクトは監視記述子によって参照され、監視はファイルまたはディレクトリのパス名によって追加されます。ディレクトリ監視は、そのディレクトリ内のすべてのファイルで発生したイベントを返します。

次の関数はウォッチを追加するために使用されます:

リーリー

fd は inotify_init() によって返されるファイル記述子、path は監視対象のパス名 (ファイル名またはディレクトリ名)、mask はイベント マスクで、それぞれヘッダー ファイル linux/inotify.h で定義されます。代表が巻き込まれた事件。イベント マスクは、通知を受け取りたい inotify イベントを変更することで、同じ方法で変更できます。 Wd は監視記述子です。

次の関数はウォッチを削除するために使用されます:

リーリー

fd は inotify_init() によって返されるファイル記述子、wd は inotify_add_watch() によって返される監視記述子です。 Ret は関数の戻り値です。

ファイル イベントは、inotify_event 構造体で表されます。この構造体は、inotify_init() によって返されたファイル記述子を介して読み取られる通常のファイル読み取り関数を使用して取得されます。

リーリー

構造体の wd は監視対象の監視記述子、mask はイベント マスク、len は名前文字列の長さ、name は監視対象のパス名、構造体の name フィールドはユーザー側からファイル名を参照するには、ファイル名は可変長であり、実際には構造に従います。ファイル名にはゼロが埋め込まれ、次のイベント構造体が 4 バイトで揃えられるようになります。 len はパディングバイトもカウントすることに注意してください。

提供された buf が十分大きい限り、読み取り呼び出しを通じて複数のイベントを一度に取得できます。

リーリー

buf は、inotify_event 構造体の配列ポインタです。BUF_LEN は、読み取られる全長を指定します。buf のサイズは、少なくとも BUF_LEN 以上である必要があります。この呼び出しによって返されるイベントの数は、BUF_LEN とその長さによって異なります。イベント内のファイル名。 Len は実際に読み取られたバイト数、つまり取得されたイベントの全長です。

inotify_init() 関数によって返されたファイル記述子 fd に対して select() または poll() を使用するか、fd に対して ioctl コマンド FIONREAD を使用して現在のキューの長さを取得できます。 close(fd) は、fd に追加されたすべての監視を削除し、必要なクリーンアップを実行します。

リーリー

3. カーネル実装の原則

カーネルでは、各 inotify インスタンスは inotify_device 構造に対応します:

リーリー

d_list はすべての inotify_device のリストを指し、i_list はすべての監視対象 i ノードのリストを指します。count は参照カウントです。dev はウォッチが配置されている inotify インスタンスに対応する inotify_device 構造体を指します。inode は i ノードを指します。 wd はウォッチに割り当てられた記述子、mask はウォッチのイベント マスクで、どのファイル システム イベントに関心があるかを示します。

结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

无论是目录还是文件,在内核中都对应一个 inode 结构,inotify 系统在 inode 结构中增加了两个字段:

struct inotify_watch {
        struct list_head        d_list; /* entry in inotify_device's list */
        struct list_head        i_list; /* entry in inode's list */
        atomic_t                count;  /* reference count */
        struct inotify_device   *dev;   /* associated device */
        struct inode            *inode; /* associated inode */
        s32                     wd;     /* watch descriptor */
        u32                     mask;   /* event mask for this watch */
};
ログイン後にコピー

d_list 指向所有 inotify_device 组成的列表的,i_list 指向所有被监视 inode 组成的列表,count 是引用计数,dev 指向该 watch 所在的 inotify 实例对应的 inotify_device 结构,inode 指向该 watch 要监视的 inode,wd 是分配给该 watch 的描述符,mask 是该 watch 的事件掩码,表示它对哪些文件系统事件感兴趣。

结构 inotify_device 在用户态调用 inotify_init() 时创建,当关闭 inotify_init()返回的文件描述符时将被释放。结构 inotify_watch 在用户态调用 inotify_add_watch()时创建,在用户态调用 inotify_rm_watch() 或 close(fd) 时被释放。

无论是目录还是文件,在内核中都对应一个 inode 结构,inotify 系统在 inode 结构中增加了两个字段:

#ifdef CONFIG_INOTIFY
 struct list_head inotify_watches; /* watches on this inode */
 struct semaphore inotify_sem; /* protects the watches list */
#endif

ログイン後にコピー

inotify_watches 是在被监视目标上的 watch 列表,每当用户调用 inotify_add_watch()时,内核就为添加的 watch 创建一个 inotify_watch 结构,并把它插入到被监视目标对应的 inode 的 inotify_watches 列表。inotify_sem 用于同步对 inotify_watches 列表的访问。当文件系统发生第一部分提到的事件之一时,相应的文件系统代码将显示调用fsnotify_* 来把相应的事件报告给 inotify 系统,其中*号就是相应的事件名,目前实现包括:

fsnotify_move,文件从一个目录移动到另一个目录fsnotify_nameremove,文件从目录中删除fsnotify_inoderemove,自删除fsnotify_create,创建新文件fsnotify_mkdir,创建新目录fsnotify_access,文件被读fsnotify_modify,文件被写fsnotify_open,文件被打开fsnotify_close,文件被关闭fsnotify_xattr,文件的扩展属性被修改fsnotify_change,文件被修改或原数据被修改有一个例外情况,就是 inotify_unmount_inodes,它会在文件系统被 umount 时调用来通知 umount 事件给 inotify 系统。

以上提到的通知函数最后都调用 inotify_inode_queue_event(inotify_unmount_inodes直接调用 inotify_dev_queue_event ),该函数首先判断对应的inode是否被监视,这通过查看 inotify_watches 列表是否为空来实现,如果发现 inode 没有被监视,什么也不做,立刻返回,反之,遍历 inotify_watches 列表,看是否当前的文件操作事件被某个 watch 监视,如果是,调用 inotify_dev_queue_event,否则,返回。函数inotify_dev_queue_event 首先判断该事件是否是上一个事件的重复,如果是就丢弃该事件并返回,否则,它判断是否 inotify 实例即 inotify_device 的事件队列是否溢出,如果溢出,产生一个溢出事件,否则产生一个当前的文件操作事件,这些事件通过kernel_event 构建,kernel_event 将创建一个 inotify_kernel_event 结构,然后把该结构插入到对应的 inotify_device 的 events 事件列表,然后唤醒等待在inotify_device 结构中的 wq 指向的等待队列。想监视文件系统事件的用户态进程在inotify 实例(即 inotify_init() 返回的文件描述符)上调用 read 时但没有事件时就挂在等待队列 wq 上。

4. 使用示例

下面是一个使用 inotify 来监视文件系统事件的例子:

#include 
#include 
#include 

_syscall0(int, inotify_init)
_syscall3(int, inotify_add_watch, int, fd, const char *, path, __u32, mask)
_syscall2(int, inotify_rm_watch, int, fd, __u32, mask)

char * monitored_files[] = {
 "./tmp_file",
 "./tmp_dir",
 "/mnt/sda3/windows_file"
};

struct wd_name {
 int wd;
 char * name;
};

#define WD_NUM 3
struct wd_name wd_array[WD_NUM];

char * event_array[] = {
 "File was accessed",
 "File was modified",
 "File attributes were changed",
 "writtable file closed",
 "Unwrittable file closed",
 "File was opened",
 "File was moved from X",
 "File was moved to Y",
 "Subfile was created",
 "Subfile was deleted",
 "Self was deleted",
 "Self was moved",
 "",
 "Backing fs was unmounted",
 "Event queued overflowed",
 "File was ignored"
};
#define EVENT_NUM 16
#define MAX_BUF_SIZE 1024
 
int main(void)
{
 int fd;
 int wd;
 char buffer[1024];
 char * offset = NULL;
 struct inotify_event * event;
 int len, tmp_len;
 char strbuf[16];
 int i = 0;
 
 fd = inotify_init();
 if (fd printf("Fail to initialize inotify.\n");
  exit(-1);
 }

 for (i=0; i"inotify_add_watch(fd," add (event- if { len) while *)buffer; inotify_event event len); len='%d.\n",' happens, printf(?Some offset="buffer;" MAX_BUF_SIZE)) buffer, while(len="read(fd," } wd_array[i].wd="wd;" exit(-1); wd_array[i].name); %s.\n?, for watch printf(?Can?t 0) (wd IN_ALL_EVENTS); wd_array[i].name, wd_array[i].name="monitored_files[i];" i++)>mask & IN_ISDIR) {
    memcpy(strbuf, "Direcotory", 11);
   }
   else {
    memcpy(strbuf, "File", 5);
   }
   printf("Object type: %s\n", strbuf);
   for (i=0; iwd != wd_array[i].wd) continue;
    printf("Object name: %s\n", wd_array[i].name);
    break;
   }
   printf("Event mask: %08X\n", event->mask);
   for (i=0; imask & (1
ログイン後にコピー

显示详细信息

以上がLinuxのinotify機能と実装原理の詳細説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

ソース:lxlinux.net
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート
私たちについて 免責事項 Sitemap
PHP中国語ウェブサイト:福祉オンライン PHP トレーニング,PHP 学習者の迅速な成長を支援します!