Every inotify event stored in the kernel queue consumes kernel memory — a finite resource shared among all processes. Without limits, a misbehaving program could create thousands of watch items or let events pile up in the queue, eventually exhausting kernel memory and crashing the system.
Linux exposes three tunable limits via files under /proc/sys/fs/inotify/. These can be read and written by the superuser.
| /proc/sys/fs/inotify/ | Default Value | What It Controls |
|---|---|---|
| max_queued_events | 16,384 | Maximum number of events that can be queued in a single inotify instance before overflow occurs. |
| max_user_instances | 128 | Maximum number of inotify instances (inotify_init() calls) that one user (real UID) can create. |
| max_user_watches | 8,192 | Maximum number of watch items that one user can have across all inotify instances combined. |
Check the current values on your system:
cat /proc/sys/fs/inotify/max_queued_events
cat /proc/sys/fs/inotify/max_user_instances
cat /proc/sys/fs/inotify/max_user_watches
If events arrive faster than your program reads them and the queue reaches max_queued_events, the kernel:
- Drops the new event (it is permanently lost)
- Generates one special
IN_Q_OVERFLOWevent
The IN_Q_OVERFLOW event has wd = -1 (no specific watch descriptor). Your program must handle this and rescan the directory tree to find out what it missed.
|
Your program must:
|
IN_Q_OVERFLOW in your event loop. Missing it leads to your program having a stale/wrong view of the file system.Read current limits (shell):
cat /proc/sys/fs/inotify/max_queued_events # e.g. 16384
cat /proc/sys/fs/inotify/max_user_instances # e.g. 128
cat /proc/sys/fs/inotify/max_user_watches # e.g. 8192
Increase limits temporarily (lost after reboot):
# Requires root (or CAP_SYS_ADMIN)
echo 65536 > /proc/sys/fs/inotify/max_queued_events
echo 524288 > /proc/sys/fs/inotify/max_user_watches
Make limits permanent (survives reboot):
# Add to /etc/sysctl.conf
fs.inotify.max_queued_events = 65536
fs.inotify.max_user_instances = 512
fs.inotify.max_user_watches = 524288
# Apply immediately without reboot:
sudo sysctl -p
max_user_watches to 524288 or more. The IDE usually tells you when it hits the limit.#include <stdio.h>
#include <stdlib.h>
void read_inotify_limit(const char *file)
{
FILE *fp = fopen(file, "r");
if (!fp) { perror(file); return; }
int value;
if (fscanf(fp, "%d", &value) == 1)
printf("%-45s = %d\n", file, value);
fclose(fp);
}
int main(void)
{
printf("=== inotify Kernel Limits ===\n");
read_inotify_limit("/proc/sys/fs/inotify/max_queued_events");
read_inotify_limit("/proc/sys/fs/inotify/max_user_instances");
read_inotify_limit("/proc/sys/fs/inotify/max_user_watches");
return 0;
}
gcc -o read_limits read_limits.c
./read_limits
# Output:
# /proc/sys/fs/inotify/max_queued_events = 16384
# /proc/sys/fs/inotify/max_user_instances = 128
# /proc/sys/fs/inotify/max_user_watches = 8192
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#define BUF_SIZE 4096
void rescan_directory(const char *path)
{
/*
* In a real program, re-read directory contents here
* to rebuild your state, since events may have been lost.
*/
printf("[RESCAN] Rebuilding view of: %s\n", path);
}
int main(void)
{
int fd, wd;
char buf[BUF_SIZE];
ssize_t numRead;
struct inotify_event *event;
char *p;
const char *watched_path = "/tmp/testdir";
fd = inotify_init1(IN_CLOEXEC);
if (fd == -1) { perror("inotify_init1"); exit(EXIT_FAILURE); }
wd = inotify_add_watch(fd, watched_path, IN_ALL_EVENTS);
if (wd == -1) { perror("inotify_add_watch"); close(fd); exit(EXIT_FAILURE); }
printf("Watching %s\n", watched_path);
for (;;) {
numRead = read(fd, buf, BUF_SIZE);
if (numRead <= 0) break;
for (p = buf; p < buf + numRead; ) {
event = (struct inotify_event *) p;
/* Check for queue overflow FIRST */
if (event->mask & IN_Q_OVERFLOW) {
fprintf(stderr, "WARNING: inotify queue overflowed! "
"Events were lost. wd=-1 for this event.\n");
/* Rescan to recover accurate state */
rescan_directory(watched_path);
/* Continue processing remaining events in buffer */
p += sizeof(struct inotify_event) + event->len;
continue;
}
/* Normal event processing */
if (event->mask & IN_CREATE && event->len > 0)
printf("Created: %s\n", event->name);
if (event->mask & IN_DELETE && event->len > 0)
printf("Deleted: %s\n", event->name);
p += sizeof(struct inotify_event) + event->len;
}
}
close(fd);
return 0;
}
You can inspect inotify fd usage for your own process via /proc:
#include <stdio.h>
#include <stdlib.h>
#include <sys/inotify.h>
#include <unistd.h>
#include <string.h>
#include <dirent.h>
/*
* Count how many entries are in /proc/self/fdinfo/
* that are inotify file descriptors.
* Each line in /proc/self/fdinfo/N that contains "inotify" is one instance.
*/
int count_inotify_fds(void)
{
DIR *dir;
struct dirent *ent;
char path[256];
char line[128];
FILE *fp;
int count = 0;
dir = opendir("/proc/self/fdinfo");
if (!dir) return -1;
while ((ent = readdir(dir)) != NULL) {
if (ent->d_name[0] == '.') continue;
snprintf(path, sizeof(path), "/proc/self/fdinfo/%s", ent->d_name);
fp = fopen(path, "r");
if (!fp) continue;
while (fgets(line, sizeof(line), fp)) {
if (strstr(line, "inotify")) { count++; break; }
}
fclose(fp);
}
closedir(dir);
return count;
}
int main(void)
{
/* Create a few inotify instances */
int fd1 = inotify_init1(IN_CLOEXEC);
int fd2 = inotify_init1(IN_CLOEXEC);
inotify_add_watch(fd1, "/tmp", IN_CREATE);
inotify_add_watch(fd2, "/var/log", IN_MODIFY);
printf("inotify instances open: %d\n", count_inotify_fds());
close(fd1);
close(fd2);
return 0;
}
Learn why the old dnotify mechanism was replaced and what makes inotify superior.
