Fundamentals 14 min read

Inside Linux inotify: How the Kernel Tracks File Changes

This article dissects Linux’s inotify mechanism, detailing the kernel data structures, system‑call flow, and functions that generate and deliver file‑system event notifications, complete with code walkthroughs and diagrams. It explains how read/write operations trigger event creation, how events are queued in inotify_device, and how user‑space processes retrieve them via read and poll interfaces.

Linux Code Review Hub
Linux Code Review Hub
Linux Code Review Hub
Inside Linux inotify: How the Kernel Tracks File Changes

Important Data Structures

inotify_device

: stores the list of watched files/directories and the event queue. inotify_dev_queue_event: called from read/write system calls to generate events.

Inotify Principle

When a user calls read or write on a watched file or directory, the kernel saves an event into the inotify_device object's event queue and wakes any process waiting on that queue.

The call stack for a read operation is:

read()
└→ sys_read()
   └→ vfs_read()
      └→ fsnotify_access()
         └→ inotify_inode_queue_event()
            └→ inotify_dev_queue_event()

inotify_dev_queue_event

This function creates an inotify_kernel_event object, inserts it into the device's event list, and wakes waiting processes.

static void inotify_dev_queue_event(struct inotify_watch *w, u32 wd,
    u32 mask, u32 cookie, const char *name, struct inode *ignored)
{
    struct inotify_user_watch *watch;
    struct inotify_device *dev;
    struct inotify_kernel_event *kevent, *last;
    watch = container_of(w, struct inotify_user_watch, wdata);
    dev = watch->dev;
    // 1. Allocate event object
    if (unlikely(dev->event_count == dev->max_events))
        kevent = kernel_event(-1, IN_Q_OVERFLOW, cookie, NULL);
    else
        kevent = kernel_event(wd, mask, cookie, name);
    // 2. Increment event counter
    dev->event_count++;
    // 3. Increase memory usage
    dev->queue_size += sizeof(struct inotify_event) + kevent->event.len;
    // 4. Add to event list
    list_add_tail(&kevent->list, &dev->events);
    // 5. Wake up waiting processes
    wake_up_interruptible(&dev->wq);
}

Core Objects

inotify_device

definition:

struct inotify_device {
    wait_queue_head_t wq;
    ...
    struct list_head events;
    ...
    struct inotify_handle *ih;
    unsigned int event_count;
    unsigned int max_events;
};
wq

: wait queue for processes waiting for events. events: list of pending inotify_kernel_event objects. ih: pointer to the associated inotify_handle. event_count / max_events: counters for queue size. inotify_kernel_event extends inotify_event with a list node and a name field:

struct inotify_kernel_event {
    struct inotify_event event;
    struct list_head list;
    char *name;
};
inotify_handle

stores the ID generator and the list of watches:

struct inotify_handle {
    struct idr idr;
    ...
    struct list_head watches;
    const struct inotify_operations *in_ops;
};
inotify_watch

represents a single watched file or directory:

struct inotify_watch {
    struct list_head h_list;   // links watches of the same inotify instance
    struct list_head i_list;   // links watches of the same inode
    struct inotify_handle *ih;
    struct inode *inode;
    __s32 wd;                  // watch descriptor
    __u32 mask;                // event mask
};

Inotify Functionality Implementation

1. inotify_init

The user‑level inotify_init call eventually invokes the kernel function sys_inotify_init:

long sys_inotify_init(void)
{
    struct inotify_device *dev;
    struct inotify_handle *ih;
    struct user_struct *user;
    struct file *filp;
    int fd, ret;
    // 1. Get an unused file descriptor
    fd = get_unused_fd();
    // 2. Get an empty file object
    filp = get_empty_filp();
    // 3. Allocate inotify_device
    dev = kmalloc(sizeof(struct inotify_device), GFP_KERNEL);
    // 4. Create inotify_handle
    ih = inotify_init(&inotify_user_ops);
    // 5. Bind handle to device
    dev->ih = ih;
    // 6. Set file operations to inotify_fops
    filp->f_op = &inotify_fops;
    // 7. Store device in private_data
    filp->private_data = dev;
    // 8. Map fd to file object
    fd_install(fd, filp);
    return fd;
}
sys_inotify_init

creates the device, the handle, links them, assigns the file operations ( read, poll, etc.) and returns the file descriptor to user space.

2. inotify_add_watch

After obtaining an inotify file descriptor, inotify_add_watch registers a path to be monitored:

long sys_inotify_add_watch(int fd, const char __user *path, u32 mask)
{
    struct inode *inode;
    struct inotify_device *dev;
    struct nameidata nd;
    struct file *filp;
    int ret, fput_needed;
    unsigned int flags = 0;
    // Get file object from fd
    filp = fget_light(fd, &fput_needed);
    // Resolve path to inode
    ret = find_inode(path, &nd, flags);
    inode = nd.dentry->d_inode;
    // Get device from file's private_data
    dev = filp->private_data;
    // Create a new watch and link it
    if (ret == -ENOENT)
        ret = create_watch(dev, inode, mask);
    return ret;
}

The function obtains the inotify_device from the file object, resolves the target path to an inode, creates an inotify_watch object and adds it to both the handle’s watches list and the inode’s watch list.

Event Notification

When a watched file is accessed, the kernel’s fsnotify_access function is called:

static inline void fsnotify_access(struct dentry *dentry)
{
    struct inode *inode = dentry->d_inode;
    u32 mask = IN_ACCESS; // access event
    if (S_ISDIR(inode->i_mode))
        mask |= IN_ISDIR;   // directory flag
    inotify_inode_queue_event(inode, mask, 0, NULL, NULL);
}

This sets the event mask ( IN_ACCESS or IN_ACCESS|IN_ISDIR) and forwards the request to inotify_inode_queue_event, which eventually calls inotify_dev_queue_event to place the event into the device’s queue and wake waiting processes.

Summary

When a user performs read/write/create/delete system calls on a watched file or directory, the kernel injects the appropriate event‑generation function, creates an inotify_kernel_event, and appends it to the inotify_device event queue.

The waiting process is awakened; it can then retrieve the pending events by invoking read (or poll) on the inotify file descriptor.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Linux kernelevent-handlingsystem callsinotifyfile monitoringkernel programming
Linux Code Review Hub
Written by

Linux Code Review Hub

A professional Linux technology community and learning platform covering the kernel, memory management, process management, file system and I/O, performance tuning, device drivers, virtualization, and cloud computing.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.