用utimes修改文件atime需传struct timeval[2],索引0为atime、1为mtime;普通用户仅能修改自有或可写文件,且noatime/relatime挂载选项会静默拦截;c++ std::filesystem不提供atime修改接口,因标准刻意限制;高精度应选utimensat(纳秒级、支持相对路径与符号链接控制)。

如何用 utimes 修改文件访问时间(atime)
Linux 的 touch 命令修改 atime 本质是调用 utimes 系统调用(glibc 封装),C++ 里没有标准库函数直接支持,必须走 POSIX 接口。注意:普通用户只能修改自己拥有或有写权限的文件;且内核挂载选项(如 noatime、relatime)可能让这个调用静默失败——不是代码错,是系统策略拦截。
实操建议:
-
utimes接受struct timeval[2]:索引 0 是 atime,索引 1 是 mtime;设为nullptr表示保持原值 - 若只想改 atime,需先用
stat读出当前 mtime,再传给utimes,否则 mtime 会被清零 - 时间戳精度只到微秒,不支持纳秒;如需更高精度,得用
utimensat(Linux 2.6.22+)
struct timeval tvs[2];
struct stat st;
if (stat("file.txt", &st) == 0) {
tvs[0] = {st.st_atim.tv_sec, st.st_atim.tv_nsec / 1000}; // atime 不变?这里可改
tvs[1] = {st.st_mtim.tv_sec, st.st_mtim.tv_nsec / 1000}; // mtime 保持
utimes("file.txt", tvs);
}
为什么 std::filesystem::last_write_time 不能改 atime
C++17 的 std::filesystem 只暴露了修改最后写入时间(mtime)的接口,last_write_time 对应的是 utimes 的第二个时间戳,完全不碰 atime。这是标准刻意设计的限制,不是遗漏——因为 atime 修改在多数场景下已不被推荐(性能开销、隐私暴露),标准库选择不提供。
常见错误现象:
- 误以为
last_write_time能控制 atime,结果 atime 完全没变 - 试图用
std::filesystem::permissions或resize_file间接触发 atime 更新 —— 这些操作本身不更新 atime,除非文件被真正读取 - 跨平台代码里硬编码
last_write_time逻辑,却在 Linux 上发现 atime 始终滞后
使用 utimensat 支持纳秒精度和相对路径
如果目标环境是较新 Linux(≥2.6.22),utimensat 比 utimes 更可靠:它支持纳秒级时间戳、能处理相对路径(避免 chdir)、且可通过 AT_SYMLINK_NOFOLLOW 避免符号链接跳转风险。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
关键差异点:
-
utimensat的时间数组类型是struct timespec[2],纳秒字段直接可用,不用除 1000 - 第一个参数可传
AT_FDCWD,第二个参数传相对路径,比拼接绝对路径更安全 - 返回 -1 且
errno == ENOSYS表示内核不支持,此时要 fallback 到utimes
struct timespec ts[2];
ts[0] = {time(nullptr), 0}; // 新 atime
ts[1] = {UTIME_OMIT}; // 保持 mtime 不变
utimensat(AT_FDCWD, "file.txt", ts, 0);
容易被忽略的挂载选项和权限陷阱
即使代码调用成功,atime 也可能纹丝不动。最常踩的坑是文件系统挂载时启用了 noatime 或 relatime。前者彻底禁用 atime 更新,后者只在 atime 早于 mtime/ctime 时才更新——这意味着你刚 touch 过,下次再 touch 就可能失效。
检查方式:
- 运行
mount | grep "noatime\|relatime"查看根分区或目标目录所在挂载点 -
touch命令本身也受此影响,所以别拿它当“正确性参考” - 普通用户无法绕过这些挂载限制,哪怕 root 也不行;唯一办法是 remount(需要权限)或换挂载点
另一个隐形坑:utimes 和 utimensat 对 symlink 的行为不同。前者默认解引用,后者可通过 flag 控制——如果目标是符号链接本身而非其指向文件,必须显式传 AT_SYMLINK_NOFOLLOW,否则修改的是链接指向的目标文件的 atime。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










