最轻量监听配置文件变化的方式是inotify,需监听in_close_write事件以确保文件写入完成;windows用readdirectorychangesw监听目录并比对文件名;重载时须保证配置对象独立分配、原子更新指针、显式迁移外部资源。

用 inotify 监听配置文件变化(Linux)
Linux 下最轻量、最直接的方式就是用 inotify,它不依赖第三方库,内核原生支持,延迟低,且只在文件真正写入完成时触发事件。
关键点在于:必须监听 IN_CLOSE_WRITE,而不是 IN_MODIFY —— 后者会在写入中途就触发,此时文件内容可能不完整,直接读取大概率出错。
- 用
inotify_init1(IN_CLOEXEC)创建监听器,避免子进程继承 fd - 对配置文件路径调用
inotify_add_watch(fd, "/etc/app.conf", IN_CLOSE_WRITE),注意路径必须是完整路径,不能是相对路径 - 每次
read()读到的struct inotify_event需检查len > 0 && event->mask & IN_CLOSE_WRITE,再触发重载 - 不要在事件回调里直接解析文件——先发信号或写管道唤醒主线程,否则阻塞监听循环
Windows 上用 ReadDirectoryChangesW 替代方案
Windows 没有 inotify,ReadDirectoryChangesW 是等效选择,但它监听的是目录,不是单个文件,且默认会报告所有子项变更,容易误触发。
常见错误是把配置文件名传给它当路径——这会失败;必须监听所在目录,并在回调中比对 FileName 字段是否等于目标文件名。
立即学习“C++免费学习笔记(深入)”;
- 调用前确保目录句柄用
FILE_FLAG_BACKUP_SEMANTICS打开 - 缓冲区大小至少设为 4KB(
sizeof(FILE_NOTIFY_INFORMATION) + 260 * sizeof(WCHAR)),否则截断导致漏事件 -
FILE_NOTIFY_CHANGE_LAST_WRITE足够,别加FILE_NOTIFY_CHANGE_SIZE——改大小不一定代表内容更新(比如清空后重写) - 返回的
FileName是宽字符、无 null 终止,需用event->FileNameLength / sizeof(WCHAR)算长度
重载配置时避免崩溃的三个硬约束
热更新不是“重新读一遍文件”那么简单。配置往往被多线程共享,结构体可能正被访问,强行替换指针或修改字段会引发未定义行为。
- 新旧配置对象必须完全独立分配(比如用
std::shared_ptr<config></config>),旧对象只能等所有使用者释放后才析构 - 全局配置指针更新必须是原子的:
std::atomic_store(&g_config, new_config),不能用普通赋值 - 如果配置含 raw 指针或引用外部资源(如日志句柄、连接池),重载时必须显式迁移或重建,不能复用旧对象里的字段
为什么不用 stat() 轮询?
轮询看似简单,但实际线上服务中几乎总是错的:CPU 白耗、延迟不可控、频繁系统调用拖慢整体吞吐。
尤其在容器环境,stat() 可能因 overlayfs 层叠导致 mtime 更新滞后或不一致;而 inotify 和 ReadDirectoryChangesW 是事件驱动,只在真实写入完成时响应。
- 轮询间隔设成 100ms,延迟上限就是 100ms;设成 1s,用户可能等半分钟才生效
- 每秒一次
stat()在 100 个进程里就是 100 次系统调用,内核调度开销明显 - 某些编辑器(如 vim)保存时先写临时文件再 rename,
stat()对原文件 mtime 无感知,直接失效
真正难的从来不是监听,而是让配置对象安全地从旧版本切换到新版本——内存布局、生命周期、线程可见性,一个没对齐就会静默崩溃。










