好的,我们来深入学习 utimes 系统调用
1. 函数介绍
在 Linux 系统中,每个文件都关联着一些重要的时间属性:
data-ad-format="fluid" data-ad-layout-key="-7k+ex-4a-9w+4a">访问时间 (atime): 文件上一次被读取的时间。
修改时间 (mtime): 文件内容上一次被修改的时间。
状态改变时间 (ctime): 文件的元数据(如权限、所有者、链接数等)上一次被改变的时间。
这些时间戳对于系统管理、备份策略、审计日志等非常重要。
utimes (Update Times) 系统调用的作用就是手动设置一个文件的访问时间 (atime) 和 修改时间 (mtime)。
简单来说,utimes 就是让你用程序来“篡改”一个文件的“上次访问时间”和“上次修改时间”。
你可能会问,为什么要手动修改这些时间呢?常见的场景有:
文件同步工具:在同步文件时,可能需要确保目标文件的时间戳与源文件完全一致。
备份和归档:某些备份工具可能需要调整文件时间戳以匹配备份时的状态。
测试:编写测试程序时,可能需要模拟文件在特定时间点被访问或修改。
修复:如果因为某些原因文件的时间戳不正确,可以手动修正。
2. 函数原型
1 | #include <sys/time.h> // 包含 utimes 函数声明和 timeval 结构体 |
3. 功能
设置由 filename 指定的文件的访问时间 (atime) 和修改时间 (mtime)。
4. 参数
filename:
const char * 类型。
指向一个以 null 结尾的字符串,表示要修改时间戳的文件的路径名。
times:
const struct timeval times[2] 类型。
一个包含两个 struct timeval 元素的数组。
times[0] 指定了新的访问时间 (atime)。
times[1] 指定了新的修改时间 (mtime)。
如果 times 指针为 NULL,则 utimes 会将 atime 和 mtime 都设置为当前时间。
struct timeval 结构体:
1 | struct timeval { |
5. 返回值
成功: 返回 0。
失败: 返回 -1,并设置全局变量 errno 来指示具体的错误原因。
6. 错误码 (errno)
EACCES: 搜索 filename 的路径组件时权限不足,或者没有写权限(因为修改时间戳通常需要写权限)。
EFAULT: filename 或 times 指向了调用进程无法访问的内存地址。
EINVAL: times 数组中的时间值无效(例如,微秒数超出范围)。
EIO: I/O 错误。
ELOOP: 解析 filename 时遇到符号链接循环。
ENAMETOOLONG: filename 太长。
ENOENT: filename 指定的文件或目录不存在。
ENOMEM: 内核内存不足。
ENOTDIR: filename 的某个前缀不是目录。
EPERM: 系统调用被阻止(例如,由 seccomp 或安全模块)。
EROFS: filename 所在的文件系统是只读的。
7. 相似函数或关联函数
utime: 一个更老的、功能类似的函数。它使用 struct utimbuf,精度只到秒。utimes 是 utime 的微秒精度版本。#include <utime.h> struct utimbuf { time_t actime; /* 访问时间 / time_t modtime; / 修改时间 */ }; int utime(const char *filename, const struct utimbuf *times);
lutimes: 与 utimes 类似,但如果 filename 是一个符号链接,它会修改符号链接本身的 atime 和 mtime,而不是它指向的目标文件。
futimes: 与 utimes 类似,但它通过已打开的文件描述符 (fd) 来指定文件,而不是文件路径。int futimes(int fd, const struct timeval tv[2]);
futimens / utimensat: 更现代的系统调用,使用 struct timespec,提供纳秒级精度,并且有更多选项(如 UTIME_OMIT, UTIME_NOW)。这些是推荐在新代码中使用的。#include <fcntl.h> // 包含 AT_FDCWD 等 #include <sys/stat.h> // 包含 timespec, UTIME_* 常量 int futimens(int fd, const struct timespec times[2]); int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);
8. 示例代码
下面的示例演示了如何使用 utimes 来修改文件的时间戳,并与其他相关函数进行比较。
1 | #define _GNU_SOURCE // 启用 GNU 扩展 |
9. 编译和运行
1 | # 假设代码保存在 utimes_example.c 中 |
10. 预期输出
1 | --- Demonstrating utimes --- |
11. 总结
utimes 是一个用于修改文件访问时间和修改时间的系统调用。
核心功能:精确设置文件的 atime 和 mtime(微秒级)。
参数:文件路径和包含两个 timeval 结构的数组。
特殊用法:传入 NULL 可将时间设置为当前时间。
相关函数:
utime: 更老的秒级精度版本。
lutimes: 修改符号链接本身的时间。
futimes: 通过文件描述符修改时间。
futimens / utimensat: 现代推荐的函数,提供纳秒精度和更多控制选项。
使用场景:文件同步、备份、测试、时间戳修复。
给 Linux 编程小白的建议:虽然 utimes 很有用,但在编写新代码时,考虑使用更新、更强大的 utimensat 或 futimens,因为它们提供了更好的精度和灵活性。