增量同步的核心判断是组合使用st_mtime和st_size:两者均一致才大概率认为内容未变,任一不同即触发同步;需注意nfs、时钟不同步及同长覆盖等误判场景,仅作快速筛选而非最终校验。

增量同步的核心判断:用 stat() 比对修改时间与大小
文件是否需要同步,不能只看文件名是否存在,关键看内容是否真的变了。最轻量、跨平台兼容性较好的做法是组合使用 st_mtime(最后修改时间)和 st_size(大小)。两者都一致,才大概率说明内容未变;任一不同,就触发同步。
注意:st_mtime 在 NFS 或某些挂载方式下可能不精确,且系统时钟不同步会导致误判;st_size 相同但内容不同(比如覆盖写入相同长度数据)的情况虽小概率但存在——所以它只是“快速筛”,不是最终校验依据。
- Linux/macOS 用
stat(),Windows 用_stat64()或GetFileInformationByHandle() - 避免仅依赖
st_mtime:有些编辑器(如 vim)会先删后写,导致时间戳重置 - 若源/目标时区或时间不同步,优先以大小 + 时间差
校验逻辑必须用内容哈希:推荐 std::hash 不够,选 xxhash 或 SHA-256
std::hash<:string></:string> 是针对内存字符串的,不能用于文件;C++ 标准库也不提供通用文件哈希函数。实际项目中应引入轻量哈希库,比如 xxhash(快、单头、无依赖),或用 OpenSSL 的 SHA256_Update() 做流式计算。
校验时机要分层:先做增量判断(mtime+size),仅对疑似变更的文件再读取并计算哈希——否则每次同步都全量读文件,IO 开销太大。
- 小文件(read() 到内存再哈希;大文件务必分块(如 64KB/块)流式计算,避免内存暴涨
- 哈希结果建议存为单独的
.syncmeta文件,格式如path|size|mtime|xxh3_64,便于下次比对 - 别用 MD5:碰撞风险在工程中已不可忽视;SHA-1 同理,至少用 SHA-256 或 xxHash v3
同步执行时避免覆盖正在写的文件:检查 open() 返回值与 errno
直接 write() 覆盖目标文件有风险:如果目标正被其他进程写入(比如日志轮转),可能破坏数据。稳妥做法是:先 open() 目标路径带 O_EXCL | O_CREAT 创建临时文件(如 file.tmp),写完 fsync(),再 rename() 原子替换。
Windows 下 rename() 对已存在文件行为不一致(可能失败),需先 DeleteFile() 再 MoveFile(),且要处理权限错误(ERROR_ACCESS_DENIED)。
- Linux 下
rename()是原子的,但目标路径父目录需有写权限 - 写临时文件前,用
access(path, W_OK)预检目标目录可写性,避免中途失败 - 务必调用
fsync()(Linux)或FlushFileBuffers()(Windows)确保数据落盘,再 rename
增量同步状态管理:别手写 JSON,用内存映射 mmap() 存校验元数据
每次同步都重新扫描全部文件并计算哈希,效率极低。需要持久化记录上次同步状态。简单项目可用 flat-file(如每行一个 path\tsize\tmtime\hash),但频繁随机查找慢;更优解是用内存映射文件模拟轻量数据库。
例如:把所有元数据序列化为固定长度结构体数组,用 mmap() 映射到内存,按路径 hash 定位——比解析 JSON 快一个数量级,且无需第三方库。
- 结构体字段对齐用
alignas(8),避免跨平台 padding 差异 - 写入前加文件锁(
flock()或LockFileEx()),防止多进程并发写损坏元数据 - 删除文件时,元数据条目不能物理擦除,而是设 flag 标记“已删除”,下次同步清理
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











