在Linux环境下进行文件操作相关的开发时,很多场景需要获取文件被最后一次访问的精确时间,传统的stat函数返回的时间精度最高仅到纳秒但部分字段未填充,而statx系统调用可以完整返回纳秒级的时间信息,下面介绍具体的实现方式。

statx系统调用介绍
statx是Linux 4.11版本引入的新系统调用,用于替代传统的stat、lstat、fstat函数,它可以返回更详细的文件元数据,其中就包含精确到纳秒级别的访问时间、修改时间、状态变更时间。要使用statx,需要包含对应的头文件,并且注意系统内核版本的支持情况。
所需头文件
使用statx需要包含以下头文件:
- sys/stat.h:提供statx结构体定义和相关常量
- fcntl.h:提供AT_*系列常量定义
- unistd.h:提供系统调用相关的基础定义
- iostream:用于C++标准输入输出
statx结构体关键字段说明
statx结构体中与文件时间相关的字段如下:
| 字段名 | 说明 |
|---|---|
| stx_atime | 文件最后一次访问时间,类型为struct statx_timestamp |
| stx_mtime | 文件最后一次修改时间,类型为struct statx_timestamp |
| stx_ctime | 文件状态最后一次变更时间,类型为struct statx_timestamp |
其中struct statx_timestamp结构体的定义如下:
struct statx_timestamp {
__u64 tv_sec; // 秒数,从Epoch(1970-01-01 00:00:00 +0000)开始计算
__u32 tv_nsec; // 纳秒数,取值范围0到999999999
__s32 __reserved; // 保留字段,无需关注
};
C++实现获取纳秒级文件访问时间
下面是完整的C++代码示例,实现获取指定文件最后一次访问的纳秒级时间信息:
#include <iostream>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
#include <cerrno>
int main() {
const char* file_path = "test.txt"; // 要获取时间的文件路径
struct statx stx;
// 调用statx系统调用,AT_FDCWD表示以当前目录为基准,不需要特殊标志,请求访问时间字段
int ret = statx(AT_FDCWD, file_path, AT_STATX_SYNC_AS_STAT, STATX_ATIME, &stx);
if (ret == -1) {
std::cerr << "获取文件信息失败,错误原因:" << strerror(errno) << std::endl;
return 1;
}
// 检查是否成功获取到访问时间字段
if (!(stx.stx_mask & STATX_ATIME)) {
std::cerr << "未获取到文件访问时间信息" << std::endl;
return 1;
}
// 输出纳秒级访问时间
std::cout << "文件最后一次访问时间:" << std::endl;
std::cout << "秒数:" << stx.stx_atime.tv_sec << std::endl;
std::cout << "纳秒数:" << stx.stx_atime.tv_nsec << std::endl;
std::cout << "完整时间戳(秒.纳秒):" << stx.stx_atime.tv_sec << "." << stx.stx_atime.tv_nsec << std::endl;
return 0;
}
代码参数说明
- 第一个参数AT_FDCWD:表示文件路径是相对于当前工作目录的,如果要使用绝对路径可以传入0,或者传入已打开的文件描述符对应的目录
- 第二个参数file_path:要查询的文件路径
- 第三个参数AT_STATX_SYNC_AS_STAT:表示同步获取文件信息,行为和传统stat一致
- 第四个参数STATX_ATIME:表示请求获取文件的访问时间字段,不请求的话结构体对应字段不会被填充
- 第五个参数&stx:用于接收返回的文件元数据的结构体指针
兼容性与注意事项
statx是较新的系统调用,使用时需要注意以下问题:
- 系统内核版本需要大于等于4.11,否则调用会返回ENOSYS错误
- 如果是低版本内核,可以考虑使用utimensat等函数间接获取,但精度可能受限
- 如果文件路径是符号链接,默认会跟随符号链接获取目标文件的信息,如果不想跟随可以加上AT_SYMLINK_NOFOLLOW标志
- 获取的时间是基于系统时钟的,不同文件系统对时间精度的支持可能不同,部分网络文件系统可能无法提供纳秒级精度
时间转换说明
如果需要将获取到的时间转换为可读的字符串格式,可以使用gmtime或者localtime函数配合strftime处理秒数部分,纳秒部分可以单独拼接:
#include <ctime>
#include <sstream>
#include <iomanip>
std::string format_nano_time(const struct statx_timestamp& ts) {
struct tm tm_time;
// 转换为本地时间
time_t sec = ts.tv_sec;
localtime_r(&sec, &tm_time);
char time_buf[64];
strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", &tm_time);
std::stringstream ss;
ss << time_buf << "." << std::setfill('0') << std::setw(9) << ts.tv_nsec;
return ss.str();
}
上述函数可以将纳秒级时间戳转换为类似2024-05-20 14:30:25.123456789的可读格式,方便日志记录或者展示使用。