在Linux系统中,每个文件的元数据都包含访问时间、修改时间、状态变更时间三类时间戳,其中访问时间就是文件最后一次被读取的时间。要获取纳秒级的精确时间戳,需要借助Linux系统提供的扩展stat结构体实现,传统的stat函数返回的时间戳精度仅到秒级,无法满足纳秒级需求。

相关结构体和函数说明
Linux系统提供了stat函数用于获取文件元数据,对应的结构体struct stat在默认情况下,时间字段是time_t类型,精度为秒。如果需要纳秒级精度,需要使用扩展的结构体struct statx,该结构体是Linux 4.11之后引入的,支持纳秒级时间戳。
相关的函数原型如下:
#include <sys/types.h> #include <sys/stat.h> #include <unistd.h> #include <fcntl.h> #include <iostream> // statx函数原型,需要包含对应的头文件 int statx(int dirfd, const char *pathname, int flags, unsigned int mask, struct statx *statxbuf);
struct statx结构体中,和访问时间相关的字段是stx_atime,它是一个struct statx_timestamp类型的结构体,包含两个字段:
tv_sec:时间戳的秒部分,类型为__u64tv_nsec:时间戳的纳秒部分,类型为__u32,取值范围是0到999999999
实现步骤
1. 包含必要的头文件
除了基础的C++头文件,还需要包含系统提供的statx相关头文件,由于部分系统可能需要显式定义特性测试宏才能使用statx,需要在代码开头添加宏定义。
2. 调用statx函数获取文件元数据
调用statx函数时,需要指定要获取的时间戳类型,通过mask参数指定STATX_ATIME即可获取访问时间的相关信息。
3. 提取纳秒级时间戳
从返回的struct statx结构体中提取stx_atime的tv_sec和tv_nsec字段,组合即可得到完整的纳秒级时间戳。
完整代码示例
以下是完整的C++示例代码,实现了获取指定文件最后一次读取的纳秒级时间戳的功能:
#define _GNU_SOURCE
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstdint>
int main() {
const char *file_path = "test.txt"; // 目标文件路径
struct statx statx_buf;
// 调用statx函数,获取文件访问时间信息
// dirfd为AT_FDCWD表示使用当前工作目录作为相对路径的基准
// flags设置为0,mask设置为STATX_ATIME表示只获取访问时间
int ret = statx(AT_FDCWD, file_path, 0, STATX_ATIME, &statx_buf);
if (ret == -1) {
perror("statx failed");
return 1;
}
// 检查是否成功获取到访问时间
if (!(statx_buf.stx_mask & STATX_ATIME)) {
std::cerr << "Failed to get access time for file: " << file_path << std::endl;
return 1;
}
// 提取秒和纳秒部分
uint64_t sec = statx_buf.stx_atime.tv_sec;
uint32_t nsec = statx_buf.stx_atime.tv_nsec;
std::cout << "File: " << file_path << std::endl;
std::cout << "Last read timestamp (seconds): " << sec << std::endl;
std::cout << "Last read timestamp (nanoseconds part): " << nsec << std::endl;
std::cout << "Full nanosecond-level timestamp: " << sec * 1000000000ULL + nsec << " nanoseconds since epoch" << std::endl;
return 0;
}
注意事项
- statx函数是Linux特有的接口,在Linux 4.11及以上版本的内核中才支持,如果系统内核版本较低,可能无法使用该接口,此时可以尝试使用
stat函数的扩展版本,部分系统提供了stat64等接口,部分结构体的st_atim字段(struct timespec类型)也包含纳秒级信息,不过需要系统支持。 - 文件的访问时间更新策略可能受文件系统挂载参数影响,比如部分系统默认挂载时设置了
noatime或者relatime参数,可能会导致访问时间不会实时更新,此时获取到的访问时间可能不是最新的读取时间。 - 编译该程序时,如果是较新的GCC版本,可能不需要额外添加编译选项,如果是旧版本,可能需要添加
-D_GNU_SOURCE编译选项,确保statx相关的定义可用。
其他方式说明
如果系统不支持statx函数,还可以尝试使用syscall直接调用系统调用号实现,不过这种方式可移植性更差,示例代码如下:
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <iostream>
#include <cstdint>
// 定义statx相关的结构体和常量,避免依赖头文件
struct statx_timestamp {
__u64 tv_sec;
__u32 tv_nsec;
__s32 __reserved;
};
struct statx {
__u32 stx_mask;
__u32 stx_blksize;
__u64 stx_attributes;
__u32 stx_nlink;
__u32 stx_uid;
__u32 stx_gid;
__u16 stx_mode;
__u16 __spare0[1];
__u64 stx_ino;
__u64 stx_size;
__u64 stx_blocks;
__u64 stx_attributes_mask;
struct statx_timestamp stx_atime;
struct statx_timestamp stx_btime;
struct statx_timestamp stx_ctime;
struct statx_timestamp stx_mtime;
__u32 stx_rdev_major;
__u32 stx_rdev_minor;
__u32 stx_dev_major;
__u32 stx_dev_minor;
__u64 __spare2[14];
};
#define STATX_ATIME 0x00000002
int main() {
const char *file_path = "test.txt";
struct statx statx_buf;
// 直接调用系统调用,系统调用号为332(不同架构可能不同,x86_64为332)
int ret = syscall(SYS_statx, AT_FDCWD, file_path, 0, STATX_ATIME, &statx_buf);
if (ret == -1) {
perror("syscall statx failed");
return 1;
}
if (!(statx_buf.stx_mask & STATX_ATIME)) {
std::cerr << "Failed to get access time" << std::endl;
return 1;
}
std::cout << "Last read nanoseconds: " << statx_buf.stx_atime.tv_sec * 1000000000ULL + statx_buf.stx_atime.tv_nsec << std::endl;
return 0;
}
这种方式不依赖系统头文件中的statx定义,但是需要确认当前系统架构对应的statx系统调用号,可移植性较差,优先推荐使用系统提供的statx函数接口。