最近开发遇到一个问题,想要获取某个文件毫秒级的修改时间。众所周知C标准库中提供的stat函数只能获取到秒级的修改时间st_mtime。相关的信息翻了个遍,最终还是神奇的ChatGPT给我了答案:

Linux 4.11及更高版本,支持statx提供了毫秒级的文件修改时间

SYNOPSIS

STRUCT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
struct statx {
__u32 stx_mask; /* Mask of bits indicating
filled fields */
__u32 stx_blksize; /* Block size for filesystem I/O */
__u64 stx_attributes; /* Extra file attribute indicators */
__u32 stx_nlink; /* Number of hard links */
__u32 stx_uid; /* User ID of owner */
__u32 stx_gid; /* Group ID of owner */
__u16 stx_mode; /* File type and mode */
__u64 stx_ino; /* Inode number */
__u64 stx_size; /* Total size in bytes */
__u64 stx_blocks; /* Number of 512B blocks allocated */
__u64 stx_attributes_mask;
/* Mask to show what's supported
in stx_attributes */

/* The following fields are file timestamps */
struct statx_timestamp stx_atime; /* Last access */
struct statx_timestamp stx_btime; /* Creation */
struct statx_timestamp stx_ctime; /* Last status change */
struct statx_timestamp stx_mtime; /* Last modification */

/* If this file represents a device, then the next two
fields contain the ID of the device */
__u32 stx_rdev_major; /* Major ID */
__u32 stx_rdev_minor; /* Minor ID */

/* The next two fields contain the ID of the device
containing the filesystem where the file resides */
__u32 stx_dev_major; /* Major ID */
__u32 stx_dev_minor; /* Minor ID */

__u64 stx_mnt_id; /* Mount ID */

/* Direct I/O alignment restrictions */
__u32 stx_dio_mem_align;
__u32 stx_dio_offset_align;
};

其中四个statx_timestamp就是我们需要的毫秒级时间戳

1
2
3
4
5
struct statx_timestamp {
__s64 tv_sec; // 秒数时间戳
__u32 tv_nsec; // tv_srchi周的纳秒数
__s32 __reserved; // 保留精度
};

FUNCTION

函数statx位于C标准库 Standard C library (libc, -lc),函数的声明和使用如下

1
2
3
4
5
6
#define _GNU_SOURCE
#include <fcntl.h>
#include <sys/stat.h>

int statx(int dirfd, const char *restrict pathname, int flags,
unsigned int mask, struct statx *restrict statxbuf);

RETURN

如果执行成功,返回0,否则返回-1并设置errno

更详细的信息请移步 LINUX MANUAL PAGE

Example

我自己测试的一个简单例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <error.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>
#include <unistd.h>

int main() {
const char *filename = "tmp_file";

// statx 结构体
struct statx buffer;

// AT_FDCWD 在当前目录下运行
// STATX_MTIME 获取修改时间
// AT_SYMLINK_NOFOLLOW 如果是链接则返回链接本身信息
int result =
statx(AT_FDCWD, filename, AT_SYMLINK_NOFOLLOW, STATX_MTIME, &buffer);

if (result == -1) {
fprintf(stderr, "Error: %m\n");
return 1;
}

// buffer.stx_mtime 记录文件的最后修改时间
// tv_sec 是秒级, tv_nsec 是毫秒级
int64_t sec = buffer.stx_mtime.tv_sec;
int micro_sec = buffer.stx_mtime.tv_nsec / 1000;
char time_str[128];

strftime(time_str, 128, "%Y-%m-%d %H:%M:%S", localtime(&sec));
fprintf(stdout, "Modification time of file %s is %s.%d\n", filename,
time_str, micro_sec);

return 0;
}

Linux测试用例

https://kgithub.com/torvalds/linux/blob/master/samples/vfs/test-statx.c