Home > Article > System Tutorial > Detailed explanation of Linux inotify function and implementation principle
Compared with MAC or Windows, the Linux desktop system still needs improvement in some aspects. In order to improve this situation, the open source community has proposed some mechanisms to allow user mode to learn about changes in the kernel or underlying hardware devices in a timely manner, so as to better manage devices and provide better services. Among them, hotplug is a mechanism for the kernel to notify user-mode applications of events about hot-plug devices, which can help desktop systems effectively manage devices. udev dynamically maintains device files under /dev, while inotify is a file system change notification mechanism that can promptly notify user-mode file system changes, such as file additions, deletions and other events. This mechanism was originally used by the famous It was introduced by the desktop search engine project beagle and has been widely used in projects such as Gamin. It should be noted that inotify is a kernel mechanism used to notify user space programs of file system changes.
In user mode, inotify is used through three system calls and file I/operations on the returned file descriptor. The first step in using inotify is to create an inotify instance:
int fd = inotify_init ();
Each inotify instance corresponds to an independent sorted queue.
The change event of the file system is an object management called watches. Each watch is a tuple (target, event mask). The target can be a file or directory. The event mask represents the inotify event that the application wants to pay attention to. , each bit corresponds to an inotify event. Watch objects are referenced by watch descriptors, and watches are added by the pathname of a file or directory. Directory watches will return events that occurred on all files in that directory.
The following function is used to add a watch:
int wd = inotify_add_watch (fd, path, mask);
fd is the file descriptor returned by inotify_init(), path is the path name of the monitored target (i.e. file name or directory name), mask is the event mask, each is defined in the header file linux/inotify.h An incident involving a representative. The event mask can be modified in the same way by changing the inotify events that you want to be notified of. Wd is the watch descriptor.
The following function is used to delete a watch:
int ret = inotify_rm_watch (fd, wd);
fd is the file descriptor returned by inotify_init(), wd is the watch descriptor returned by inotify_add_watch(). Ret is the return value of the function.
File events are represented by an inotify_event structure, which is obtained using the usual file reading function read through the file descriptor returned by inotify_init()
:
int inotify_init (void); int inotify_add_watch (int fd, const char *path, __u32 mask); int inotify_rm_watch (int fd, __u32 mask);
The wd in the structure is the watch descriptor of the monitored target, mask is the event mask, len is the length of the name string, name is the path name of the monitored target, the name field of the structure is a stub, it is just To reference the filename from the user side, the filename is of variable length and actually follows the structure, and the filename will be padded with zeros to enable the next event structure to be 4-byte aligned. Note that len also counts padding bytes.
Multiple events can be obtained at one time through the read call, as long as the provided buf is large enough.
size_t len = read (fd, buf, BUF_LEN);
buf is an array pointer of the inotify_event structure. BUF_LEN specifies the total length to be read. The size of buf must be at least no less than BUF_LEN. The number of events returned by this call depends on BUF_LEN and the length of the file name in the event. Len is the number of bytes actually read, that is, the total length of the event obtained.
You can use select() or poll() on the file descriptor fd returned by the function inotify_init(), or you can use the ioctl command FIONREAD on fd to get the length of the current queue. close(fd) will delete all watches added to fd and do necessary cleanup.
int inotify_init (void); int inotify_add_watch (int fd, const char *path, __u32 mask); int inotify_rm_watch (int fd, __u32 mask);
In the kernel, each inotify instance corresponds to an inotify_device structure:
struct inotify_device { wait_queue_head_t wq; /* wait queue for i/o */ struct idr idr; /* idr mapping wd -> watch */ struct semaphore sem; /* protects this bad boy */ struct list_head events; /* list of queued events */ struct list_head watches; /* list of watches */ atomic_t count; /* reference count */ struct user_struct *user; /* user who opened this dev */ unsigned int queue_size; /* size of the queue (bytes) */ unsigned int event_count; /* number of pending events */ unsigned int max_events; /* maximum number of events */ u32 last_wd; /* the last wd allocated */ };
d_list points to the list of all inotify_device, i_list points to the list of all monitored inodes, count is the reference count, dev points to the inotify_device structure corresponding to the inotify instance where the watch is located, inode points to the inode to be monitored by the watch, wd is the descriptor assigned to the watch, and mask is the event mask of the watch, indicating which file system events it is interested in.
结构 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 上。
下面是一个使用 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
显示详细信息
The above is the detailed content of Detailed explanation of Linux inotify function and implementation principle. For more information, please follow other related articles on the PHP Chinese website!