inotify需主动read()获取事件,非自动通知;须用select()/epoll_wait()等待fd可读后循环read(),避免漏事件或缓冲区溢出;in_modify仅表示写入动作,非保存完成,大文件需结合in_close_write判断;监控目录不递归触发子文件in_modify,须单独添加watch或捕获in_moved_to/in_create;内核队列默认16384字节,可调max_queued_events。

Linux 下用 inotify 监控文件变化要手动轮询事件
inotify 本身不主动通知你“文件被改了”,它只提供一个文件描述符,你需要自己 read() 它来获取事件。漏读、缓冲区溢出、未处理 IN_Q_OVERFLOW 都会导致丢事件。
典型错误是开完 inotify_init1() 就直接 read() 一次然后退出——这根本不是监控,只是快照。
- 必须用
select()、poll()或epoll_wait()等待 fd 可读,再循环read()直到返回 0 或EAGAIN -
IN_MODIFY触发的是写入动作,不是保存完成;大文件分块写时可能收到多次IN_MODIFY,需结合IN_CLOSE_WRITE判断写入结束 - 监控目录时,
IN_MODIFY不会触发子文件变化,必须对每个目标文件单独inotify_add_watch(),或用IN_MOVED_TO/IN_CREATE捕获新建/重命名后重新打开 - 注意
inotify的内核队列长度默认仅 16384 字节,大并发写场景下容易溢出,可通过/proc/sys/fs/inotify/max_queued_events调整
Windows 下 ReadDirectoryChangesW 必须配 OVERLAPPED + 异步等待
同步调用 ReadDirectoryChangesW() 会阻塞线程,且一次只能等一个目录;更糟的是,如果传的缓冲区太小,函数直接失败返回 FALSE,GetLastError() 是 ERROR_INSUFFICIENT_BUFFER,但很多人忽略这个错误码,以为没变化。
它也不告诉你“哪个文件被改”,只告诉你“有东西变了”,具体路径得靠解析返回的 FILE_NOTIFY_INFORMATION 链表——而这个结构是变长的,没有终止符,必须靠 NextEntryOffset 跳转,手动遍历一不留神就内存越界。
- 缓冲区建议至少 8KB,小于 1KB 极易触发
ERROR_INSUFFICIENT_BUFFER - 必须用
FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OVERLAPPED打开目录句柄,否则调用失败 - 异步模式下不能直接
WaitForSingleObject()等hEvent,得用GetQueuedCompletionStatus()配合 IOCP,否则在多目录监控时无法扩展 -
ReadDirectoryChangesW()对符号链接、硬链接行为不一致:默认不跟踪跨卷重命名,且对 NTFS 压缩/加密文件属性变更可能不触发FILE_NOTIFY_CHANGE_LAST_WRITE
C++ 跨平台封装要注意事件语义差异
Linux inotify 和 Windows ReadDirectoryChangesW 的事件粒度和触发时机根本不同:前者能精确到单个文件的 IN_CLOSE_WRITE,后者只报告目录级变更,且对原子写(如编辑器先写 tmp 再 rename)只能捕获 FILE_ACTION_RENAMED_OLD_NAME + FILE_ACTION_RENAMED_NEW_NAME,中间没有“内容变更”事件。
这意味着:想可靠检测“文件内容是否变化”,不能只依赖这些 API 的事件,必须在收到事件后立刻 stat() / GetFileAttributesEx() 比对 st_mtime 或 ftLastWriteTime,甚至对关键文件做轻量级 hash(如前 1KB CRC32)防误报。
- 不要假设
IN_MODIFY== 文件已持久化,SSD 缓存、ext4 journal 模式都可能导致st_mtime更新早于数据落盘 - Windows 上若进程以
GENERIC_READ权限打开文件,ReadDirectoryChangesW仍能收到事件;但若另一进程用FILE_SHARE_WRITE打开并写入,你自己的ReadFile()可能读到脏数据——监控和读取要加外部同步 - macOS 用户常误用
FSEvents,但它不支持单文件监控,只能目录,且事件合并策略激进(例如连续 5 次保存可能只报 1 次),不适合需要精确变更点的场景
别忘了权限、符号链接和卸载风险
监控会失败,不是因为代码写错,而是环境不让——比如容器里挂载的 tmpfs 不支持 inotify,或 Windows 网络共享目录不触发 ReadDirectoryChangesW(除非启用 SMB 3.1.1+ 并配置 SMBServer 的 EnableOplocks)。
还有更隐蔽的问题:符号链接目标被修改,但你监控的是链接本身,inotify 默认不跟随;而 Windows 上若监控路径含 .. 或软链接,ReadDirectoryChangesW 可能静默失败且不报错。
- Linux 上用
inotify_add_watch(fd, path, mask)前,先realpath()解析路径,避免监控失效 - Windows 上检查目录是否可监控:
GetVolumeInformation()看dwFlags是否含FILE_SUPPORTS_OBJECT_IDS(非必需)但更要确认FILE_SUPPORTS_REMOTE_NOTIFIES(对网络路径) - 所有平台都要处理监控源消失的情况:
inotify中文件被unlink()后 watch 自动移除,但不会通知你;Windows 下目录被移动或卷被拔出,ReadDirectoryChangesW()后续调用会返回ERROR_DIRECTORY或ERROR_INVALID_HANDLE,必须重试初始化
真正难的从来不是调 API,而是理解它在什么条件下不工作,以及怎么从“好像变了”里确认“确实变了”。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











