Linux inotif Mechanism and example code

Source: Internet
Author: User
Tags inotify
Linux inotif mechanism

First, preface:

As we all know, Linux desktop system and MAC or Windows have a lot of unsatisfactory place, in order to improve this situation, the open source community proposed that the user state needs the kernel to provide some mechanism, so that the user can be in a timely manner to know the kernel or the underlying hardware devices, so as to better manage the device, Providing better services to users, such as HotPlug, Udev and INotify, is what this demand has spawned. Hotplug is a kernel-to-user application advisory on hot-swappable equipment, the mechanism of the occurrence of some events, the desktop system can use it to effectively manage the device, Udev dynamically maintain/dev device files, inotify is a file system change notification mechanism, such as file increase, The deletion of such events can be immediately known to the user, the mechanism is the famous Desktop Search engine project Beagle introduced, and in the Gamin and other projects are applied.

In fact, a similar mechanism exists before inotify called Dnotify, but it has many drawbacks:

1. For each directory you want to monitor, users need to open a file descriptor, so if you need to monitor more directories, it will cause many file descriptors to open, especially if the monitored directory on removable media (such as discs and USB drives) will cause the file system to be umount, because using Dnotify Application Open File descriptor is used in the file system.

2. Dnotify is directory-based, it can only get directory change events, of course, the changes in the directory file will affect its directory to trigger the directory Change event, but in order to know which file changes through directory events, it is necessary to cache many stat structure data.

3. The Dnotify interface is very unfriendly, it uses signal.

Designed for alternative Dnotify, Inotify overcomes the flaws of dnotify and provides a better, concise and powerful notification mechanism for file changes:

1. Inotify does not need to open the file descriptor on the target being monitored, and if the target is being monitored on removable media, the watch corresponding to the monitored target will be automatically deleted after umount the file system on that media, and a Umount event will be generated.

2. Inotify can either monitor the file or monitor the directory.

3. Inotify uses system calls rather than SIGIO to notify file system events.

4. Inotify uses file descriptors as interfaces, so you can use the usual file I/O operations Select and poll to monitor file system changes.

Inotify file system events that can be monitored include:

In_access, that is, the file is accessed
In_modify, File is write
In_attrib, file attributes are modified, such as chmod, chown, touch, etc.
In_close_write, writable file is CLOSE
In_close_nowrite, non-writable files are CLOSE
In_open, File is OPEN
In_moved_from, files are removed, such as MV
In_moved_to, files are moved, such as MV, CP
In_create, creating a new file
In_delete, files are deleted, such as RM
In_delete_self, self-deletion, that is, an executable file deletes itself when executed
In_move_self, self-moving, that is, an executable file moves itself at execution time
In_unmount, the host file system is Umount
In_close, file is closed, equivalent to (In_close_write | In_close_nowrite)
In_move, file is moved, equivalent to (In_moved_from | IN_MOVED_TO)

Note: The above mentioned files also include directories.

Second, the user interface

In the user state, INotify is used by three system calls and file i/operations on the returned file descriptor, and the first step in using INotify is to create the INotify instance:

int fd = Inotify_init ();

Each inotify instance corresponds to a separate sorted queue.

File system Change event is called a watches object management, each watch is a two-tuple (target, event mask), the target can be a file or directory, the event mask represents the INotify event that the application wants to focus on, and each bit corresponds to a inotify event. The Watch object is referenced by the watch descriptor and watches is added through the path name of the file or directory. Directory watches will return events that occurred above all the 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 pathname of the target being monitored (that is, the file name or directory name), mask is the event mask, and the event represented by each bit is defined in the header file Linux/inotify.h. You can modify the event mask in the same way, changing the INotify event that you want 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 a function.
A file event is represented by a inotify_event structure, which is obtained by using the usual file read function read by the file descriptor returned by Inotify_init ().

struct Inotify_event {    __s32      wd;       /* Watch Descriptor */    __u32      mask;      /* Watch Mask */    __u32      cookie;     /* Cookie to synchronize the events */    __u32      len;      /* Length (including nulls) of the name */    char      name[0];    /* Stub for possible name */};

The WD in the structure is the watch descriptor for 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 pile, it simply refers to the user aspect of the file name, the file name is variable length, It is actually immediately following the structure, and the file name is populated with 0 to enable the next event structure to be 4-byte aligned. Note that Len also counts the number of padding bytes.
Multiple events can be obtained at one time through a read call, provided the BUF is large enough.

size_t len = Read (FD, buf, Buf_len);

BUF is an array pointer to a inotify_event structure, Buf_len specifies the total length to read, the BUF size is at least less than Buf_len, and the number of events returned by the call depends on the Buf_len and the length of the file name in the event. Len is the number of bytes actually read, which is the total length of the obtained event.
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) deletes all watch added to the FD and does the necessary cleanup.

    int inotify_init (void); int inotify_add_watch (int fd, const char *path, __u32 mask); int inotify_rm_watch (int fd, __U32 m ASK);

Three, the core implementation principle

In the kernel, each of the INotify instances corresponds to a 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 */};


Wq is waiting for the queue, the process blocked by the read call is hung on the wait queue, and IDR is used to map the watch descriptor to the corresponding Inotify_watch,sem for synchronizing access to the structure, and events is the list of occurrences of the inotify instance, by that in All events monitored by the otify instance are inserted into this list after the occurrence, watches is the watch list for inotify instance monitoring, Inotify_add_watch inserts the newly added watch into the list, count is the reference count, and user Describes the user who created the INotify instance, queue_size represents the number of bytes of the event queue for the INotify instance, Event_count is the number of events in the event list, max_events is the maximum number of events allowed, LAST_WD is the last allocated Wat CH Descriptor.

Each watch corresponds to a inotify_watch 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 the list of all Inotify_device, i_list points to the list of all monitored inode, count is the reference count, and dev points to the inotify_device structure of the inotify instance where the watch is located , the inode points to the watch to monitor the INODE,WD is assigned to the watch descriptor, mask is the event mask of the watch, indicating which file system events it is interested in.

The struct Inotify_device is created when the user state calls Inotify_init () and is freed when the file descriptor returned by Inotify_init () is closed. The struct Inotify_watch is created when the user state calls Inotify_add_watch () and is freed when the user state calls Inotify_rm_watch () or Close (FD).
The inotify system adds two fields to the inode structure, regardless of whether it is a directory or a file, which corresponds to an inode structure in the kernel:

#ifdef config_inotify  struct list_head  inotify_watches;/* Watches on the inode */  struct semaphore  Inotify_sem;  /* Protects the Watches list */#endif

Inotify_watches is the watch list on the monitored target, and each time the user calls Inotify_add_watch (), the kernel creates a inotify_watch structure for the added watch and inserts it into the inode for the monitored target. List of inotify_watches. Inotify_sem is used to synchronize access to the Inotify_watches list. When one of the events mentioned in the first part of the file system occurs, the corresponding file system code will display the call to fsnotify_* to report the corresponding event to

INotify system, where the * number is the corresponding event name, the current implementation includes:

Fsnotify_move, moving files from one directory to another

Fsnotify_nameremove, files are removed from the directory
Fsnotify_inoderemove, self-removal
Fsnotify_create, creating a new file
Fsnotify_mkdir, create a new directory
Fsnotify_access, file is read
Fsnotify_modify, File is written
Fsnotify_open, file is open
Fsnotify_close, file is closed
Fsnotify_xattr, the extended properties of the file have been modified
Fsnotify_change, the file is modified or the original data is modified

One exception is Inotify_unmount_inodes, which is called when the file system is umount to notify the Umount event to the inotify system.
The notification functions mentioned above are finally called Inotify_inode_queue_event (Inotify_unmount_inodes call inotify_dev_queue_event directly), The function first determines whether the corresponding inode is monitored, which is accomplished by looking at whether the Inotify_watches list is empty, if the inode is not monitored, does nothing, returns immediately, and, conversely, traverses the inotify_watches list, See if the current file manipulation event is monitored by a watch, and if it is, call inotify_dev_queue_event, otherwise, return. The function Inotify_dev_queue_event first determines whether the event is a repetition of the previous event, discards the event, and returns it, otherwise it determines whether the INotify instance is the Inotify_device event queue overflow, if overflow, An overflow event is generated, otherwise a current file manipulation event is generated, which is built by Kernel_event, Kernel_event creates a inotify_kernel_event structure, and then inserts the structure into the corresponding inotify_ The events list of the device, and then wakes up the wait queue that waits for WQ in the inotify_device structure to point to. The user-state process that wants to monitor file system events is called Read on the INotify instance (that is, the file descriptor returned by Inotify_init (), but no events are hung on the wait queue Wq.

Iv. Examples of Use

Here is an example of using INotify to monitor file system events:

#include <linux/unistd.h> #include <linux/inotify.h> #include <errno.h>_syscall0 (int, inotify_init ) _syscall3 (int, inotify_add_watch, int, FD, const char *, PATH, __u32, mask) _syscall2 (int, inotify_rm_watch, int, FD, __U3  2, 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 is accessed", "File was modified", "Fil E attributes were changed "," Writtable file Closed "," unwrittable file Closed "," file was opened "," file was moved fro   M X "," File was moved-Y "," Subfile was created "," Subfile is deleted "," Self is deleted "," Self is moved "," ", "Backing FS is unmounted", "Event queued overflowed", "File was ignored"}; #define Event_num 16#define max_buf_size 10  the 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 < 0) {printf ("Fail to initialize inotify.\n");  Exit (-1);    } for (i=0; i<wd_num; i++) {wd_array[i].name = monitored_files[i];    WD = Inotify_add_watch (FD, Wd_array[i].name, in_all_events);      if (WD < 0) {printf ("Can ' t add watch for%s.\n", wd_array[i].name);    Exit (-1);  } WD_ARRAY[I].WD = WD;    } while (len = read (fd, buffer, max_buf_size)) {offset = buffer;    printf ("Some event happens, Len =%d.\n", Len);    event = (struct inotify_event *) buffer; while (((char *) event-buffer) < Len) {if (Event->mask & In_isdir) {memcpy (strbuf, "Direcotory",      11);      } else {memcpy (strbuf, "File", 5);      } printf ("Object type:%s\n", strbuf);        for (i=0; i<wd_num; i++) {if (event->wd! = 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; i<event_num; i++) {if (event_array[i][0] = = ' + ') continue;        if (Event->mask & (1<<i)) {printf ("Event:%s\n", Event_array[i]);      }} tmp_len = sizeof (struct inotify_event) + event->len;       event = (struct inotify_event *) (offset + tmp_len);    Offset + = Tmp_len; }  }}


The program will monitor all file system events that occur in the current directory under File Tmp_file and directory Tmp_dir in the current directory, and it will also monitor file system events that occur on file/mnt/sda3/windows_file, noting that/mnt/sda3 is The mount point of the SATA hard drive partition 3.

The attentive reader may notice that the program first uses _SYSCALLN to declare inotify system calls, because these system calls are introduced in the latest stable kernel 2.6.13, and glibc does not implement the version of the library functions for these system calls, so In order to be able to use these system calls in the program, these new systems must be declared through _syscalln, where N is the actual number of parameters to invoke for the system to be declared. It is also important to note that the system's header files must match the kernel being launched, in order for the above program to compile successfully, the 2.6.13 kernel header files (including include/linux/*, include/asm/*, and include/asm-generic) must be made available. /*) within the header file search path, and is the header file path for the first priority search, because _syscalln needs to use the linux/unistd.h and asm/unistd.h in these header files, which contain the system call numbers of the INotify three system calls __nr_ Inotify_init, __nr_inotify_add_watch and __nr_inotify_rm_watch.

Therefore, to successfully compile this program, simply copy the user-compiled kernel's header file to the path where the program is located, and compile with the following command:

$GCC-o inotify_example-i. inotify_example.c

Note: The current directory should contain a file directory of Linux, ASM, and Asm-generic three compiled 2.6.13 kernels, ASM is a link, so it is necessary to copy ASM and Asm-arch when copying ASM header files (for the x86 platform should be ASM -i386). Then, in order to run the program, you need to create files in the current directory Tmp_file and directory Tmp_dir, for/mnt/sda3/windows_file files, the user needs according to their own actual situation, may be/mnt/dosc/windows_file, That is,/mnt/dosc is a FAT32 windows hard disk, so users need to modify the/mnt/sda3 according to their actual situation when compiling the program. Windows_file is a file created on a mount drive that must be created in order to run the program.
Here are some results from the author running this program on Redhat 9.0:

When running this program, perform cat./tmp_file on another virtual terminal, and the output of this program is:

Some event happens, Len = 48.Object type:fileobject name:./tmp_fileevent Mask:00000020event:file was openedobject type : FileObject name:./tmp_fileevent Mask:00000001event:file was accessedobject type:fileobject name:./tmp_fileEvent Mas k:00000010event:unwrittable file closed

The above events clearly state that the CAT directive performs file open and close operations, and of course both the open and close operations are access operations, and any action on the file is an access operation.

In addition, running VI/tmp_file, found that vi actually copied a copy of the file when it was edited, and the copy was manipulated before it was saved. Run VI/tmp_file, modify and save the exit, found that VI actually deleted the original file when saving the changes and changed the copy file name to the original file name. Note that the event "file was ignored" means that the system takes the watch from the inotify instance to the watch list, because the file was deleted. Readers can execute their own commands individually: echo "abc" >./tmp_file, rm-f tmp_file, LS tmp_dir, cd Tmp_dir;touch c.txt, RM c.txt, Umount/mnt/sda3 (real Users need to use their own mount-point pathname), and then analyze the results. Umount triggers two events, one indicating that the file has been deleted or not present, and another watch that represents the file is removed from the watch list.

Thank you for reading, hope to help everyone, thank you for the support of this site!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.