Interview Questions โ€“ inotify

 

๐ŸŽฏ Interview Questions โ€“ inotify
25 questions covering concepts, code, edge cases, and comparisons
How to Use This Page

Click each question to reveal the answer. Questions are marked by difficulty:

Easy Basic concepts ย  Medium API usage and design ย  Hard Edge cases and tricky behavior

Section 1 โ€“ Fundamentals
Q1 What is inotify and why was it introduced in Linux? Easy

inotify is a Linux kernel mechanism that allows user-space programs to monitor files and directories for changes (creation, deletion, modification, renames, opens, closes, etc.). It was introduced in kernel 2.6.13 to replace the older dnotify mechanism.

Key reasons it was introduced: dnotify used signals for notification (complicated), required one file descriptor per watched directory (wasteful), only monitored directories (not individual files), and did not report which specific file changed inside a watched directory.

Q2 What are the four steps to use inotify? Easy
  1. Call inotify_init() or inotify_init1() to create an inotify instance and get a file descriptor.
  2. Call inotify_add_watch(fd, path, mask) to register each path you want to monitor and get a watch descriptor (wd).
  3. Call read(fd, buf, size) in a loop to receive inotify_event structures as events occur.
  4. Call close(fd) when done. This automatically removes all associated watches.
Q3 What is the difference between inotify_init() and inotify_init1()? Easy

inotify_init() takes no arguments. inotify_init1(flags) accepts flags to modify behavior:

  • IN_CLOEXEC: Sets the close-on-exec flag on the fd. The fd will be automatically closed if the process calls exec(). Prevents fd leaks into child processes.
  • IN_NONBLOCK: Makes future read() calls non-blocking โ€” they return immediately with EAGAIN if no events are available, instead of blocking.

Best practice: Always use inotify_init1(IN_CLOEXEC) in new code.

Q4 What does the watch descriptor (wd) returned by inotify_add_watch() represent? Easy

A watch descriptor is a non-negative integer that uniquely identifies a particular watch item within one inotify instance. When an event occurs, the wd field in the inotify_event structure tells you which watched path triggered the event.

Since multiple paths can be watched via a single inotify fd, your program must maintain a mapping from wd values back to pathnames (e.g., a lookup table or hash map). The wd is unique per inotify instance, not globally.

Q5 Is inotify monitoring recursive? How do you monitor an entire directory tree? Easy

No, inotify monitoring is not recursive. Watching /home/ravi does NOT automatically watch subdirectories like /home/ravi/docs.

To monitor an entire directory tree, you must:

  1. Use nftw() (file tree walk) to enumerate all directories in the tree.
  2. Call inotify_add_watch() for each directory found.
  3. When a new subdirectory is created (detected via IN_CREATE | IN_ISDIR), immediately add a new watch for it.
  4. When a directory is deleted, remove its watch from your bookkeeping.

This is the exercise given at the end of Chapter 19 of “The Linux Programming Interface”.

Section 2 โ€“ Events
Q6 What is the difference between IN_DELETE and IN_DELETE_SELF? Medium
Event When It Fires
IN_DELETE You are watching a directory, and a file/subdirectory inside that directory is deleted. The name field contains the deleted filename.
IN_DELETE_SELF The watched object itself (file or directory) was deleted. The name field is empty (len=0).

Example: You watch /tmp/dir. Deleting /tmp/dir/foo.txt triggers IN_DELETE (name=”foo.txt”). Deleting /tmp/dir itself triggers IN_DELETE_SELF (no name), followed by IN_IGNORED.

Q7 What is the cookie field in inotify_event used for? Medium

The cookie field is used to link two related events that are part of the same rename operation. When a file is renamed:

  • An IN_MOVED_FROM event is generated for the source directory
  • An IN_MOVED_TO event is generated for the destination directory
  • Both events have the same non-zero cookie value

Your program matches the cookie in IN_MOVED_FROM with the cookie in IN_MOVED_TO to know that these are the two halves of one rename, not an unrelated delete + create.

For all other events (not rename), cookie is zero.

Q8 Why would you use IN_CLOSE_WRITE instead of IN_MODIFY to detect a file save? Medium

IN_MODIFY fires on every single write() call to the file. A single “save” in a text editor can trigger dozens of IN_MODIFY events as it writes the file incrementally.

IN_CLOSE_WRITE fires once when a file that was open for writing is closed. This is a much cleaner signal that “a complete write operation is done”.

Use case: A daemon that should reload its config file on save. Using IN_MODIFY could cause it to reload dozens of times during one editor save. Using IN_CLOSE_WRITE triggers exactly once after the save is complete.

Caveat: Some editors (like vim) write to a temp file and then rename it. In that case, watch for IN_MOVED_TO or IN_CREATE instead, since the original file may be replaced atomically.

Q9 What does IN_ISDIR indicate, and how do you check for it? Easy

IN_ISDIR is an output-only bit set in the mask field of returned events. When it is set, it means the event subject is a directory, not a regular file.

You check it with bitwise AND alongside the primary event:

if (event->mask & IN_CREATE) {
    if (event->mask & IN_ISDIR)
        printf("Subdirectory created: %s\n", event->name);
    else
        printf("File created: %s\n", event->name);
}

This is important for recursive monitoring: when you see IN_CREATE | IN_ISDIR, you know to add a new watch for that new subdirectory.

Q10 What is IN_ONESHOT and when would you use it? Medium

IN_ONESHOT is a control flag that causes the watch to be automatically removed after the first event fires. It gives you a one-shot notification.

Use cases:

  • Wait for a file to be created (as a synchronization primitive)
  • Scripts that need to react to exactly one change and then exit
  • Test code that should only capture one event

Note: After an IN_ONESHOT watch fires, it is silently removed โ€” no IN_IGNORED event is generated (unlike explicitly calling inotify_rm_watch()).

Section 3 โ€“ inotify_event Structure & Reading
Q11 Why must you add event->len when advancing through an event buffer? Medium

The inotify_event structure has a variable-length name field. The size of the fixed header is sizeof(struct inotify_event), but each event may be followed by 0 or more bytes for the filename.

The len field tells you how many bytes are allocated for the name field (including the null terminator and any padding). To move to the next event:

p += sizeof(struct inotify_event) + event->len;

If you only advance by sizeof(struct inotify_event), you will misinterpret the bytes that belong to the filename as the next event header, causing garbage reads and likely a crash.

Q12 What is the minimum buffer size you should pass to read() on an inotify fd? Medium

The minimum safe buffer to guarantee at least one event can be read:

#include <limits.h>
size_t min = sizeof(struct inotify_event) + NAME_MAX + 1;
/* = 16 + 255 + 1 = 272 bytes on Linux */

NAME_MAX (255) is the maximum filename length. +1 is for the null terminator.

In practice, always use a much larger buffer (4096 or 8192 bytes) so that one read() returns multiple events efficiently, reducing system call overhead.

If the buffer is too small for the next queued event, read() returns -1 with errno = EINVAL.

Q13 When is the name field in inotify_event populated, and when is it empty? Medium

Populated (len > 0): When the event is for a file inside a watched directory. The name field contains just the filename (not the full path).

Empty (len = 0): When the event is for the watched object itself. For example, if you are directly watching a file and it gets modified, or if the watched directory is itself renamed.

Always check event->len > 0 before using event->name:

if (event->len > 0)
    printf("File: %s\n", event->name);
else
    printf("Event on the watched path itself\n");
Q14 Can the inotify file descriptor be used with select() or epoll()? Why is this useful? Medium

Yes. The inotify fd behaves like any regular Linux file descriptor and can be passed to select(), poll(), epoll_ctl(), and ppoll(). The fd becomes readable when events are available.

This is extremely useful in event-driven programs that need to react to both:

  • File system events (via inotify fd)
  • Network events (via socket fds)
  • Timer events
  • Signals (via signalfd)

You can multiplex all of these into a single epoll loop, avoiding the need for multiple threads.

Section 4 โ€“ Limits & Kernel Internals
Q15 What are the three /proc files that control inotify limits, and what are their default values? Easy

All three files are under /proc/sys/fs/inotify/:

File Default What It Limits
max_queued_events 16,384 Events that can queue in one inotify instance before overflow
max_user_instances 128 inotify instances per user UID
max_user_watches 8,192 Watch items per user UID across all instances
Q16 What happens when the inotify event queue overflows? How should your program handle it? Hard

When the queue reaches max_queued_events, the kernel:

  1. Discards all subsequent incoming events (they are permanently lost)
  2. Generates one IN_Q_OVERFLOW event with wd = -1

Your program’s correct response:

  1. Detect IN_Q_OVERFLOW in your event loop
  2. Treat your current cached view of the directory as potentially stale
  3. Rescan the entire watched directory tree to rebuild accurate state
  4. Log or alert that events were lost

Ignoring IN_Q_OVERFLOW leads to your program having an incorrect view of the filesystem, which can cause serious bugs (e.g., a backup tool missing files, a build system not recompiling changed sources).

Q17 Does inotify event coalescing affect your program’s correctness? Hard

Possibly, yes. The kernel coalesces consecutive identical events: if two consecutive events in the queue have the same wd, mask, cookie, and name, the second is dropped and merged into the first.

Example: If a file is modified 50 times in rapid succession, you may receive far fewer than 50 IN_MODIFY events.

Implication: You cannot use inotify to count how many times an operation occurred. You can only know that it occurred at least once. Your program should always respond by checking the current state of the file (read it, stat it), not by counting events.

This is the correct design pattern: treat each event as a signal to re-examine the file, not as an exact record of every change.

Section 5 โ€“ dnotify vs inotify
Q18 What are the main limitations of dnotify compared to inotify? Medium
  1. Signals: dnotify uses signals (SIGIO) for notification, which complicates application design and makes library use nearly impossible. inotify uses a file descriptor.
  2. Directory-only: dnotify can only monitor directories. inotify can monitor individual files or directories.
  3. No filename in event: dnotify tells you a directory changed but not which file. inotify provides the exact filename in the event.
  4. One fd per directory: dnotify keeps the watched directory open, consuming one fd per watch and preventing filesystem unmounting. inotify needs one fd for all watches.
  5. Unreliable: dnotify can miss events in some scenarios. inotify has a reliable ordered queue.
Q19 How does dnotify set up directory monitoring? What system call does it use? Medium

dnotify uses the fcntl() system call with the F_NOTIFY command:

int dir_fd = open("/tmp/mydir", O_RDONLY);
fcntl(dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MODIFY | DN_MULTISHOT);

When something changes in the directory, the kernel sends a real-time signal to the process (configurable via F_SETSIG, defaulting to SIGIO). The signal handler must then scan the directory to figure out what changed.

The DN_MULTISHOT flag makes it persistent (fires more than once). Without it, the watch fires only once and must be re-registered.

All dnotify documentation is in the fcntl(2) manual page under F_NOTIFY.

Section 6 โ€“ Code-Based & Tricky Questions
Q20 What is wrong with this code? Hard
/* Buggy code โ€” what is the bug? */
for (p = buf; p < buf + numRead; ) {
    event = (struct inotify_event *) p;
    printf("wd=%d\n", event->wd);
    p += sizeof(struct inotify_event);   /* BUG HERE */
}

Bug: The pointer advance p += sizeof(struct inotify_event) does not account for the variable-length name field. If any event has a non-zero len (i.e., it contains a filename), the pointer will point into the middle of that filename on the next iteration, misinterpreting garbage bytes as the next event header.

Fix:

p += sizeof(struct inotify_event) + event->len;  /* CORRECT */
Q21 How would you watch a symbolic link itself (not its target)? Hard

By default, inotify_add_watch() dereferences symbolic links โ€” it watches the file the symlink points to, not the symlink itself.

To watch the symlink itself, use the IN_DONT_FOLLOW flag in the mask:

wd = inotify_add_watch(fd, "/tmp/mylink", IN_CREATE | IN_DONT_FOLLOW);

With IN_DONT_FOLLOW, the kernel monitors the symlink inode. You will receive events when the symlink is deleted, moved, or its attributes change โ€” but not events on the target file.

This flag was added in kernel 2.6.15.

Q22 What happens if you call inotify_add_watch() twice on the same path with the same inotify fd? Hard

If you call inotify_add_watch() on a path that is already in the watch list for the same fd, it does not create a second watch item. Instead, it modifies the existing watch โ€” the mask is replaced with the new mask, and the same wd is returned.

If you want to add events to an existing watch (rather than replace the mask), use the IN_MASK_ADD flag:

/* First watch: monitor CREATE events */
wd = inotify_add_watch(fd, "/tmp/dir", IN_CREATE);

/* Later: also monitor DELETE events, keeping IN_CREATE */
inotify_add_watch(fd, "/tmp/dir", IN_DELETE | IN_MASK_ADD);
/* Now watching for IN_CREATE | IN_DELETE */

Without IN_MASK_ADD, the second call would replace the mask, and IN_CREATE would no longer be monitored.

Q23 What event is generated when you call inotify_rm_watch(), and when is it NOT generated? Hard

When a watch is removed, the kernel generates an IN_IGNORED event for that watch descriptor.

IN_IGNORED is generated in these cases:

  • Application explicitly calls inotify_rm_watch()
  • The watched object is deleted (kernel implicitly removes the watch)
  • The filesystem containing the watched object is unmounted

Exception: IN_IGNORED is NOT generated when a watch set up with IN_ONESHOT is automatically removed after its first event fires. This is a subtle but important difference.

Q24 A developer uses cat > /tmp/dir/file.txt and types some text, then presses Ctrl+D. What events will inotify generate, in order? Hard

Assuming /tmp/dir is watched with IN_ALL_EVENTS:

  1. IN_CREATE โ€” “file.txt” is created
  2. IN_OPEN โ€” “file.txt” is opened (cat opened it for writing)
  3. IN_MODIFY โ€” file content written (when text is typed and buffered)
  4. IN_CLOSE_WRITE โ€” file is closed after writing (Ctrl+D causes EOF, cat closes fd)

This matches the example output shown in the book: the demo program reading from the inotify fd showed exactly these events in this order.

Q25 How would you implement a simple “wait for file to appear” function using inotify? Medium

Watch the parent directory for IN_CREATE events, then check if the created filename matches the one you are waiting for:

#include <sys/inotify.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>  /* for dirname, basename */

/* Wait until 'filepath' appears. Returns 0 on success. */
int wait_for_file(const char *filepath)
{
    char *dir  = dirname(strdup(filepath));
    char *file = basename(strdup(filepath));

    int fd = inotify_init1(IN_CLOEXEC);
    if (fd == -1) return -1;

    /* Watch the parent directory, not the file itself (it doesn't exist yet) */
    int wd = inotify_add_watch(fd, dir, IN_CREATE);
    if (wd == -1) { close(fd); return -1; }

    char buf[4096];
    for (;;) {
        ssize_t n = read(fd, buf, sizeof(buf));
        char *p;
        for (p = buf; p < buf + n; ) {
            struct inotify_event *ev = (struct inotify_event *) p;
            if (ev->mask & IN_CREATE && ev->len > 0)
                if (strcmp(ev->name, file) == 0) {
                    close(fd);
                    return 0;  /* Found it! */
                }
            p += sizeof(struct inotify_event) + ev->len;
        }
    }
}

int main(void) {
    printf("Waiting for /tmp/ready.flag ...\n");
    wait_for_file("/tmp/ready.flag");
    printf("File appeared!\n");
    return 0;
}

Quick Reference โ€“ inotify API Cheat Sheet
/* Headers */
#include <sys/inotify.h>
#include <limits.h>     /* NAME_MAX */

/* Create instance */
int fd = inotify_init1(IN_CLOEXEC);           /* recommended */
int fd = inotify_init();                       /* legacy */

/* Add/modify watch */
int wd = inotify_add_watch(fd, path, mask);   /* returns watch descriptor */

/* Remove watch */
inotify_rm_watch(fd, wd);

/* Read events */
char buf[sizeof(struct inotify_event) + NAME_MAX + 1];
ssize_t n = read(fd, buf, sizeof(buf));

/* Parse events */
for (char *p = buf; p < buf + n; ) {
    struct inotify_event *e = (struct inotify_event *) p;
    /* process e */
    p += sizeof(struct inotify_event) + e->len;   /* MUST include e->len */
}

/* Cleanup */
close(fd);   /* removes all watches automatically */

/* /proc limits */
// /proc/sys/fs/inotify/max_queued_events   (default: 16384)
// /proc/sys/fs/inotify/max_user_instances  (default: 128)
// /proc/sys/fs/inotify/max_user_watches    (default: 8192)

/* Common event masks */
// IN_CREATE    IN_DELETE    IN_MODIFY    IN_CLOSE_WRITE
// IN_MOVED_FROM  IN_MOVED_TO  IN_DELETE_SELF  IN_ALL_EVENTS
// IN_IGNORED   IN_ISDIR     IN_Q_OVERFLOW   IN_UNMOUNT

You’ve Completed the inotify Series!

Go back to review any topic, or start the next chapter on EmbeddedPathashala.

โ† Back to Index Start from Part 1

Leave a Reply

Your email address will not be published. Required fields are marked *