1. inotify Main Functions
It is a mechanism used by the kernel to notify user space program file system changes.
As we all know, Linux desktop systems are much less satisfactory than Mac or windows. To improve this situation, the open-source community proposes that the kernel should provide some mechanisms for user States, so that the user State can know in time what happened to the kernel or underlying hardware device, so as to better manage the device and provide users with better services, such
Hotplug, udev, and inotify are the results of this demand. Hotplug is a kernel mechanism for notifying user-Mode Applications About some events of hot swapping devices. The desktop system can use it to effectively manage devices, inotify is a file system change notification mechanism that allows you to dynamically maintain Device Files under/dev. Events such as file addition and deletion can be immediately known to users, this mechanism was introduced by Beagle, a famous Desktop Search Engine Project, and applied to projects such as gamin.
2. User Interface
In the user State, inotify is used through three system calls and file I/operations on the returned file descriptor. The first step of using inotify is to create an inotify instance:
int fd = inotify_init (); |
Each inotify instance corresponds to an independent ordered queue.
The file system change event is called an object management of watches. Each watch is a binary group (destination, event mask), and the target can be a file or directory, the event mask indicates the inotify event to be followed by the application. Each bit corresponds to an inotify event. The watch object is referenced by the watch descriptor, And the watches object is added by the file or directory path name. The watches directory returns the events that occur on all files in the 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 (that is, the file name or directory name). Mask is the event mask.
Linux/inotify. h defines each event. You can modify the event mask in the same way, that is, change the inotify event to be notified. 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 (), and 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 by the file descriptor returned by inotify_init () using the common file read function read.
:
struct inotify_event { __s32 wd; /* watch descriptor */ __u32 mask; /* watch mask */ __u32 cookie; /* cookie to synchronize two events */ __u32 len; /* length (including nulls) of name */ char name[0]; /* stub for possible name */}; |
In the structure, WD 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, and the name field of this structure is a pile, it only references the file name for the user. The file name is variable and follows the structure. The file name will be filled with 0 so that the next event structure can be 4-byte aligned. Note that Len also counts the number of padding bytes.
You can obtain multiple events at a 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 Buf size must be at least smaller than buf_len. The number of events returned by this call depends on the length of buf_len and the file name in the event. Len is the number of bytes actually read, that is, the total length of the obtained event.
You can use select () or Poll () on the file descriptor FD returned by the inotify_init () function, or use the ioctl command fionread on FD to get the length of the current queue. Close (FD) will delete all the watches added to FD and perform 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); |
3. kernel implementation principle
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 a list composed of all inotify_devices. I _list points to a list composed of all monitored inode. Count indicates the reference count. Dev points to the inotify_device structure corresponding to the inotify instance of the watch, 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.
The structure inotify_device is created when the user State calls inotify_init (). When the file descriptor returned by inotify_init () is disabled, it is released. The structure inotify_watch is created when the user State calls inotify_add_watch () and is released when the user State calls inotify_rm_watch () or close (FD.
Both directories and files correspond to an inode structure in the kernel. The inode System adds two fields to the inode structure:
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 points to a list composed of all inotify_devices. I _list points to a list composed of all monitored inode. Count indicates the reference count. Dev points to the inotify_device structure corresponding to the inotify instance of the watch, 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.
The structure inotify_device is created when the user State calls inotify_init (). When the file descriptor returned by inotify_init () is disabled, it is released. The structure inotify_watch is created when the user State calls inotify_add_watch () and is released when the user State calls inotify_rm_watch () or close (FD.
Both directories and files correspond to an inode structure in the kernel. The inode System adds two fields to the inode structure:
#ifdef CONFIG_INOTIFYstruct list_headinotify_watches; /* watches on this inode */struct semaphoreinotify_sem;/* protects the watches list */#endif |
Inotify_watches is the Watch List on the monitored target. Whenever you call inotify_add_watch (), the kernel creates an inotify_watch structure for the added watch, insert it to the inotify_watches list of inode corresponding to the monitored target. Inotify_sem is used to synchronize access to the inotify_watches list. When the first part of the event occurs in the file system, the corresponding file system code will display the call to fsnoop _*
To report the corresponding event to the inotify system, where the "*" is the corresponding event name. The current implementation includes:
Fsnotify_move: The file is moved from one directory to another. The file is deleted from the directory, fsnotify_create is deleted, fsnotify_mkdir is created, fsnotify_access is created, and the file is read fsnotify_modify, the file is written to fsnotify_open, the file is opened to fsnotify_close, the file is closed to fsnotify_xattr, the file's extended attribute is modified to fsnotify_change, and the file is modified or the original data is modified.
Inotify_unmount_inodes, which is called to notify the inotify system of the umount event when the file system is umount.
The preceding notification functions call inotify_unmount_inodes to directly call inotify_dev_queue_event. This function first checks whether the corresponding inode is monitored. This function is implemented by checking whether the inotify_watches list is empty, if inode is not monitored and nothing is done, return immediately. Otherwise, traverse the inotify_watches list to see if the current file operation event is under a watch.
Monitoring. If yes, call inotify_dev_queue_event. Otherwise, return. The inotify_dev_queue_event function first checks whether the event is a duplicate of the previous event. If yes, It discards the event and returns it. Otherwise, it determines whether the inotify instance, that is, whether the event queue of inotify_device overflows. If yes, an overflow event is generated. Otherwise, a file operation event is generated. These events are constructed through kernel_event. kernel_event creates an inotify_kernel_event structure and inserts the structure into the corresponding inotify_device.
And then wake up the waiting queue that WQ points to in the inotify_device structure. If the user-state process that wants to monitor file system events calls read on the inotify instance (that is, the file descriptor returned by inotify_init (), but there is no event, the user-state process hangs on the WQ waiting queue.
4. Example
The following is an example of using inotify to monitor File System Events:
#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 3struct 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 1024int 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 < 0) {printf("Fail to initialize inotify.\n");exit(-1);}for (i=0; i<WD_NUM; wd="inotify_add_watch(fd," add (event- if { len) < buffer) - *)event (((char 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<len;event = (struct inotify_event *)(offset + tmp_len); offset += tmp_len;}}} |
From: http://linux.chinaitlab.com/kernel/391147_4.html