应优先用修改时间与大小组合判断文件变更,仅对两者均一致的文件做内容哈希校验;替换目标文件需先写临时文件再原子rename;遍历时须捕获权限异常并跳过符号链接;状态快照应存于用户目录,含路径、mtime、size、inode等字段。

同步逻辑该用什么策略判断文件变更
直接对比文件内容效率低,尤其大文件。实际应优先用元数据(修改时间 + 大小)做快速筛选,再对元数据一致的文件做内容哈希(如 std::hash 或 SHA256)校验。注意:Windows 和 Linux 下 stat::st_mtime 精度不同(Windows 是 100ns,Linux 通常是 1s 或纳秒级),跨平台时不能只依赖时间戳相等就跳过同步。
- 若源文件
mtime新于目标,且大小不同 → 必须复制 - 若
mtime相同但大小不同 → 文件可能被截断或覆盖,需内容比对 - 若两者都相同 → 可跳过(除非业务要求强制校验哈希)
如何安全地替换目标文件避免中断读取
直接 std::filesystem::copy 覆盖正在被其他进程读取的文件,可能导致读取失败或数据损坏。正确做法是先写入临时文件(如 target.tmp),再用 std::filesystem::rename 原子替换。注意:rename 在同一文件系统内是原子的,跨分区会失败并抛出 std::filesystem::filesystem_error。
- 临时文件名建议用
std::tmpnam或拼接target_path.string() + ".tmp" - 写完后调用
std::filesystem::permissions(tmp_path, std::filesystem::perms::owner_read | std::filesystem::perms::owner_write)确保权限不继承错误 - 替换前检查目标目录是否可写,否则
rename会失败而非静默忽略
递归遍历目录时怎么处理符号链接和权限拒绝
std::filesystem::recursive_directory_iterator 默认不跟随符号链接,也不会自动跳过无权限目录 —— 遇到 std::filesystem::filesystem_error(如 Permission denied)会直接抛异常中断遍历。必须显式捕获并跳过。
- 构造迭代器时传入
std::filesystem::directory_options::skip_permission_denied - 对每个
entry调用entry.is_symlink()判断,按需决定是否follow_symlink(通常同步工具默认不跟随,避免循环引用) - 遍历时用
try { ... } catch (const std::filesystem::filesystem_error& e) { continue; }捕获单条路径异常,别让整个同步流程崩溃
增量同步需要记录状态,该存到哪、存什么
每次全量扫描太慢,必须维护一个本地状态快照(如 JSON 或二进制)。关键字段至少包括:文件路径(相对根目录)、st_mtime、st_size、st_ino(inode,用于检测硬链接/重命名)。不要存绝对路径,否则迁移工具位置就失效。
- 状态文件建议放在用户配置目录(
std::filesystem::user_home() / ".myrsync" / "state.bin"),而非项目目录 - 写状态前先写临时文件,再
rename替换,防止中断导致状态损坏 - 如果发现某文件在状态中存在但磁盘已删除,说明它被用户手动删了 —— 同步工具应默认不恢复(除非开启
--restore-deleted)
mtime 乱序,以及 Windows 上的长路径和保留名称(如 CON、AUX)。这些边界情况不写测试很难暴露。C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











