inotify_init() 返回-1主因是内核未启用inotify或fd耗尽;需检查/proc/sys/fs/inotify/max_user_watches等参数、ulimit -n限制,并确认errno为EMFILE或ENOSPC。

inotify_init() 返回 -1 怎么排查
直接调用 inotify_init() 失败,大概率是内核没开支持或 fd 耗尽。先确认 /proc/sys/fs/inotify/max_user_instances 和 max_user_watches 是否过低——很多 Docker 容器或最小化系统默认只给 128 或 8192,监控上百个文件立刻爆掉。
实操建议:
- 用
cat /proc/sys/fs/inotify/max_user_watches查当前值,临时提高:sudo sysctl -w fs.inotify.max_user_watches=524288 - 检查进程 fd 限制:
ulimit -n,不够就改/etc/security/limits.conf或 systemd 的LimitNOFILE - 别漏掉
errno == EMFILE(fd 耗尽)和ENOSPC(watch 数超限),strace跟一下最准
监控多个路径时要不要为每个路径调用 inotify_add_watch()
要。一个 inotify_init() 实例可挂多个 watch,但必须对每个目录/文件单独调用 inotify_add_watch(),传入不同 pathname 和事件掩码。不能靠递归自动监听子目录——inotify 本身不递归,得自己遍历 + 注册。
常见错误现象:只监听了 /var/log,结果 /var/log/nginx/access.log 改动没触发——因为子路径没显式加 watch。
立即学习“C++免费学习笔记(深入)”;
实操建议:
- 用
IN_CREATE | IN_MODIFY | IN_MOVED_TO覆盖新建、修改、重命名场景,别只写IN_MODIFY - 监听目录时加
IN_ONLYDIR防止误把同名文件当目录打开失败 - 注意返回值:成功返回 watch descriptor(非负整数),失败返回 -1,记得检查
errno
read() 读 inotify 事件时卡住或丢事件怎么办
read() 默认阻塞,且事件缓冲区只有内核里那块固定大小的环形队列(通常几 KB)。一旦应用处理慢、没及时 read,新事件就会覆盖旧事件——不是“丢”,是被挤掉了。
实操建议:
- 用
poll()或epoll()管理 inotify fd,避免单线程卡死;别用 while(1) + read() 硬等 - 每次
read()必须循环解析,因为一次 read 可能返回多个struct inotify_event,它们紧挨着存,长度不固定 - 事件结构体里的
len字段是name字符串长度(不含 \0),不是整个 event 大小;总长要用sizeof(struct inotify_event) + event->len算 - 如果需要可靠,得在用户态做事件队列+去重(比如按 wd+cookie 判断是否同一 mv 操作)
程序退出前忘记清理 inotify 资源会怎样
进程退出时内核自动释放 fd 和所有 watch,不会泄漏。但若程序长期运行(比如守护进程),又反复 add_watch 不删,最终 hit max_user_watches 就再也加不了了——这时候 inotify_add_watch() 开始返回 -1,且 errno == ENOSPC。
实操建议:
- 用完某个 watch 后,显式调用
inotify_rm_watch(fd, wd);尤其在 reload 配置、切换监控路径时 - 不要依赖 RAII 自动析构——C++ 对象销毁不一定等于 fd 关闭,得确保
close(inotify_fd)被调用 - 调试时用
find /proc/*/fd -lname "inotify" 2>/dev/null | wc -l看全局 inotify 实例数
inotify 本身很轻,但组合成推送系统时,真正的复杂点不在监听,而在事件合并(比如 vim 保存触发 3 次 modify)、路径规范(符号链接怎么处理)、以及推送通道的可靠性(网络抖动时事件要不要落盘)。这些没法靠 inotify 自己解决,得在它上面搭一层状态机。



















