inotify扩展必须手动安装启用,否则inotify_init()报错;轮询需用stat()全字段比对并刷新缓存;inotify_read()无超时参数,须转流后设超时;监控子目录需递归添加watch并处理动态创建。

inotify 扩展必须安装并启用,否则 inotify_init() 直接报错
PHP 本身不自带 inotify 支持,依赖系统 inotify API 和 PHP 的 inotify 扩展。常见错误是直接调用 inotify_init() 却提示 Call to undefined function inotify_init() —— 这说明扩展没装。
确认方式:php -m | grep inotify;若无输出,需手动安装(Ubuntu/Debian):sudo apt install php-inotify,然后重启 PHP-FPM 或 Apache;CentOS/RHEL 需编译或启用 EPEL 源后 yum install php-pecl-inotify。注意:PHP 8.0+ 默认不再捆绑该扩展,必须显式安装。
扩展启用后,inotify_init() 返回资源句柄,后续所有操作(inotify_add_watch()、inotify_read())都依赖它。别忘了 inotify_close() 清理资源,否则长期运行会泄漏 inotify 实例(Linux 系统有 per-user 限制,默认 128 个)。
轮询方案不是“简单 sleep”,关键在 filemtime() + stat() 组合判断
纯靠 filemtime() 判断修改时间,容易漏掉「文件内容没变但属性变了」或「秒级精度下连续修改」的问题。更稳妥的做法是对比完整 stat() 结构中的 mtime、size、ino(inode)、dev(设备号),尤其当监控符号链接或 NFS 挂载目录时。
实操建议:
- 用
clearstatcache(true, $path)强制刷新 stat 缓存,避免 PHP 自带的缓存干扰判断 - 不要用
sleep(1)硬等,改用usleep(100000)(100ms)减少响应延迟,同时避免 CPU 空转 - 对多个文件轮询时,把路径和上次
stat()结果存在数组里,每次只比对变更项,别全量重读 - 如果监控的是日志类高频写入文件,考虑用
tail -f+popen()替代轮询,更轻量
inotify_read() 是阻塞调用,但超时控制只能靠 stream_set_timeout()
很多人以为 inotify_read() 支持第 2 个超时参数,其实它没有 —— 它会一直卡住直到事件发生。要实现可控等待(比如最长等 5 秒),必须把 inotify 资源转成流:$stream = fopen('php://fd/' . $fd, 'r')(其中 $fd 是 inotify_init() 返回的 fd),再用 stream_set_timeout($stream, 5)。之后 fread($stream, 8192) 就会按设定超时返回。
否则,进程可能被卡死,无法响应信号或执行清理逻辑。这点在守护进程或 CLI 长任务中特别致命。
inotify 不递归,监控子目录必须手动遍历 + inotify_add_watch()
inotify_add_watch($inotify_fd, $path, IN_CREATE | IN_MODIFY) 只作用于单层目录。如果要监控 /var/log/app/ 及其所有子目录下的新增/修改,得自己递归扫描当前目录树,对每个子目录单独调用一次 inotify_add_watch(),并记录 watch descriptor(wd)与路径映射关系。
注意两个坑:
- 递归深度大时,watch 数量可能迅速逼近系统上限(
/proc/sys/fs/inotify/max_user_watches),需提前调整,例如:echo 524288 | sudo tee /proc/sys/fs/inotify/max_user_watches - 新建子目录不会自动被监控 —— 你得监听父目录的
IN_CREATE | IN_ISDIR事件,捕获到新目录后,再动态调用inotify_add_watch()加入监控
递归监控逻辑本身不复杂,但状态维护(增删 wd、路径映射、事件解析)容易出错,生产环境建议用 inotifywait(inotify-tools)封装或改用 watchdog(Python)这类成熟方案,PHP 原生做太重。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











