Before inotify, Linux had a mechanism called dnotify (directory notify), available since kernel 2.4. It let applications monitor directories for changes. However, dnotify had so many limitations that the Linux kernel developers created inotify as a complete replacement. dnotify is now considered obsolete.
You will still encounter dnotify references in older codebases and interview questions, so it is worth understanding what it did wrong.
dnotify uses the fcntl() system call with the F_NOTIFY command:
#include <fcntl.h>
#include <signal.h>
/* Step 1: Open the directory you want to watch */
int dir_fd = open("/tmp/mydir", O_RDONLY);
/* Step 2: Request notification via signal (default: SIGIO) */
fcntl(dir_fd, F_NOTIFY, DN_CREATE | DN_DELETE | DN_MODIFY);
/* When something changes in /tmp/mydir, your process receives SIGIO */
/* You must set up a signal handler to process it */
dnotify sends a real-time signal (or SIGIO) to your process when something in the watched directory changes. Your signal handler then has to figure out what changed.
| Feature | โ dnotify | โ inotify |
|---|---|---|
| Notification method | Signals (SIGIO or real-time signal) โ complicates design | File descriptor โ clean read() API |
| Granularity | Directory only โ you know something changed but not which file | Directory OR individual file โ exact filename returned |
| File descriptor overhead | Requires an open fd for each watched directory. Busy fs can’t be unmounted. | One fd for the entire inotify instance, regardless of how many paths are watched |
| Unmount-safe | No โ open fd keeps filesystem busy, blocking umount | Yes โ sends IN_UNMOUNT event and removes watch |
| Event detail | Tells you an event type (create/delete/modify) but not the filename involved | Returns the exact filename, event type, and cookie for renames |
| Reliability | Unreliable in some circumstances โ events can be missed | Reliable ordered event queue with overflow detection |
| Library use | Very difficult โ signal handlers conflict with the calling program’s signal setup | Works cleanly in libraries โ just another fd |
| Available since | Kernel 2.4 | Kernel 2.6.13 |
| Status today | Obsolete โ avoid in new code | Recommended โ use this |
The biggest design flaw of dnotify is using signals for notification. Here is why that is bad:
printf(), malloc(), or most library functions safely. This makes it very hard to do anything useful in response to a dnotify event.volatile sig_atomic_t flags and is easy to get wrong.read() call or integrating with select()/epoll naturally.With dnotify, you must keep an open file descriptor for each directory you watch:
|
โ dnotify โ 1 fd per directory
|
โ
inotify โ 1 fd for everything
|
The dnotify approach hits the per-process file descriptor limit (ulimit -n, usually 1024 or 4096) very quickly on large directory trees. inotify has no such problem.
Here is the same task โ “tell me when a file is created in /tmp/mydir” โ implemented both ways:
โ dnotify approach (old, complex, signal-based):
#include <fcntl.h>
#include <signal.h>
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <unistd.h>
static int dir_fd;
static char known_files[1000][256];
static int known_count = 0;
/* Signal handler โ very limited in what it can do here */
static void dnotify_handler(int sig)
{
/* We know SOMETHING changed but NOT WHAT.
Must rescan the entire directory! */
DIR *d = opendir("/tmp/mydir"); /* Warning: not safe in signal handler! */
struct dirent *entry;
while ((entry = readdir(d)) != NULL) {
/* Compare against known_files to find what's new */
/* This is complex, error-prone, and racy */
}
closedir(d);
}
int main(void)
{
signal(SIGIO, dnotify_handler);
/* Must open the directory with a real fd */
dir_fd = open("/tmp/mydir", O_RDONLY);
if (dir_fd == -1) { perror("open"); return 1; }
/* Request notification */
fcntl(dir_fd, F_SETSIG, SIGIO);
fcntl(dir_fd, F_NOTIFY, DN_CREATE | DN_MULTISHOT);
/* Main loop โ must handle async signal interruptions */
for (;;) { pause(); }
close(dir_fd);
return 0;
}
โ inotify approach (modern, clean, fd-based):
#include <sys/inotify.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#define BUF_SIZE 4096
int main(void)
{
int fd = inotify_init1(IN_CLOEXEC);
inotify_add_watch(fd, "/tmp/mydir", IN_CREATE);
char buf[BUF_SIZE];
ssize_t n = read(fd, buf, BUF_SIZE); /* blocks until event */
struct inotify_event *event = (struct inotify_event *) buf;
/* We know EXACTLY which file was created */
printf("Created: %s\n", event->name);
close(fd);
return 0;
}
The inotify version is shorter, cleaner, and gives you the exact filename directly, with no signal handler complexity.
| dnotify Limitation | inotify Solution |
|---|---|
| Uses signals โ complicated and library-unfriendly | Uses file descriptor โ fits naturally into any event loop |
| Only watches directories, not individual files | Watches files and directories |
| Doesn’t tell you which file changed inside the directory | Gives you the exact filename in the name field |
| Requires one open fd per watched directory | One fd for all watches in one inotify instance |
| Open fd prevents unmounting of filesystem | No open fds on watched paths; graceful unmount handling |
| Unreliable in some edge cases | Reliable ordered queue with overflow reporting |
Test your knowledge with 20+ interview questions on inotify covering all topics.
