Understanding Linux dnotify: Predecessor to inotify

This article examines the legacy dnotify (Directory Notification) subsystem in the Linux operating system, detailing its architectural implementation, event-handling mechanics, and historical significance. Introduced in Linux kernel 2.4, dnotify was the kernel's first native solution for monitoring filesystem changes. Below is a breakdown of how the mechanism functions, how applications interact with it via system calls, and the technical limitations that necessitated its replacement by inotify.

The Core Architecture of dnotify

The dnotify mechanism operates entirely through the standard file descriptor architecture and the fcntl system call. Unlike modern monitoring interfaces that use dedicated file descriptors for event queues, dnotify attaches event-monitoring masks directly to directory file descriptors already opened by a user-space process.

To initialize monitoring, a program opens a target directory using open() to obtain a file descriptor and then issues an fcntl command:

fcntl(fd, F_NOTIFY, DN_MODIFY | DN_CREATE | DN_DELETE | DN_MULTISHOT);

The third argument is a bitmask of events that the kernel should observe. Supported event flags include:

Event Delivery via Signals

Instead of using a readable event stream, dnotify relies on POSIX signals to alert user-space applications. By default, the kernel emits a SIGIO signal to the process when an event matches the configured bitmask.

Because SIGIO provides no contextual information about the event, developers typically reconfigured the notification signal to a real-time signal (ranging from SIGRTMIN to SIGRTMAX) using fcntl(fd, F_SETSIG, signum). Real-time signals allow queuing and carry an accompanying siginfo_t structure. This structure passes the file descriptor of the modified directory to the signal handler via the si_fd field, allowing the application to identify which watched directory generated the alert.

Operational Limitations of dnotify

While dnotify provided an improvement over continuous filesystem polling, several fundamental architectural constraints limited its scalability and reliability:

  1. Lack of Granularity: The signal delivered by dnotify only indicates that a change occurred within a directory; it does not identify which specific file inside the directory was modified, added, or deleted. The user application must maintain an in-memory snapshot of directory contents and run stat() or readdir() across the entire directory upon receiving a signal to determine what changed.
  2. File Pinning and Unmounting: dnotify requires keeping an open file descriptor on every monitored directory. An open file descriptor locks the corresponding mount point, preventing removable media from being unmounted and complicating storage management.
  3. Signal Complexity and Race Conditions: Handling asynchronous signals in complex, multi-threaded applications introduces severe race conditions and reentrancy challenges. Rapid bursts of filesystem activity could overflow standard signal queues, leading to lost events.
  4. No Direct File Monitoring: dnotify can only monitor directories, not individual files. To track changes to a single file, an application must monitor the parent directory and filter out irrelevant events.

Transition to inotify and fanotify

Due to these inefficiencies, dnotify was superseded by inotify in Linux kernel 2.6.13. inotify addressed these weaknesses by abandoning signal-based delivery in favor of a dedicated file descriptor that outputs a structured event stream via read(), provides specific filenames with events, and avoids pinning filesystems. Although retained in modern kernels for backward compatibility, dnotify is considered obsolete and has been entirely replaced in production software by inotify and fanotify.