utimes函数详解 链接到标题
1. 函数介绍 链接到标题
utimes函数是Linux系统中用于修改文件访问时间和修改时间的函数。它是utime函数的增强版本,提供了微秒级的时间精度。可以把utimes想象成一个"高精度时间修改器",它能够精确地设置文件的时间戳到微秒级别。
与utime函数相比,utimes的主要优势在于它支持更高精度的时间设置,能够满足对时间精度要求较高的应用场景。这在需要精确控制文件时间戳的系统中特别有用。
使用场景:
- 文件备份和恢复时保持精确时间戳
- 软件构建系统中的依赖关系管理
- 测试环境中的时间模拟
- 文件系统维护和同步
- 需要微秒级精度的时间控制应用
2. 函数原型 链接到标题
#include <sys/time.h>
int utimes(const char *filename, const struct timeval times[2]);
3. 功能 链接到标题
utimes函数的主要功能是修改指定文件的访问时间和修改时间,支持微秒级精度的时间设置。
4. 参数 链接到标题
-
filename: 文件路径名
- 类型:const char*
- 含义:要修改时间戳的文件路径名
-
times: 时间数组
- 类型:const struct timeval[2]
- 含义:包含新时间戳的数组
- times[0]:访问时间(atime)
- times[1]:修改时间(mtime)
- 如果为NULL,则设置为当前时间
- timeval结构体定义:
struct timeval { time_t tv_sec; // 秒数 suseconds_t tv_usec; // 微秒数 };
5. 返回值 链接到标题
- 成功: 返回0
- 失败: 返回-1,并设置errno错误码
- EACCES:权限不足
- EFAULT:filename指向无效内存
- EIO:I/O错误
- ELOOP:符号链接循环
- ENAMETOOLONG:路径名过长
- ENOENT:文件不存在
- ENOTDIR:路径前缀不是目录
- EPERM:操作不被允许
- EROFS:文件系统只读
6. 相似函数或关联函数 链接到标题
- utime(): 基础时间修改函数(秒级精度)
- futimes(): 通过文件描述符修改时间
- lutimes(): 修改符号链接时间(不跟随链接)
- futimens(): 通过文件描述符修改时间(纳秒级精度)
- utimensat(): 相对于目录文件描述符修改时间
- stat(): 获取文件状态信息
- touch命令: 命令行下的时间修改工具
7. 示例代码 链接到标题
示例1:基础utimes使用 - 简单时间修改 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
// 创建测试文件
int create_test_file(const char* filename, const char* content) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建文件失败");
return -1;
}
if (write(fd, content, strlen(content)) == -1) {
perror("写入文件失败");
close(fd);
return -1;
}
close(fd);
printf("创建文件: %s\n", filename);
return 0;
}
// 显示文件详细时间信息
void show_detailed_file_times(const char* filename) {
struct stat file_stat;
if (stat(filename, &file_stat) == -1) {
perror("获取文件状态失败");
return;
}
printf("文件 %s 的详细时间信息:\n", filename);
// 显示访问时间
struct tm* atime_tm = localtime(&file_stat.st_atime);
printf(" 访问时间: %04d-%02d-%02d %02d:%02d:%02d",
atime_tm->tm_year + 1900, atime_tm->tm_mon + 1, atime_tm->tm_mday,
atime_tm->tm_hour, atime_tm->tm_min, atime_tm->tm_sec);
#ifdef st_atim
printf(".%06ld", (long)file_stat.st_atim.tv_nsec / 1000);
#endif
printf("\n");
// 显示修改时间
struct tm* mtime_tm = localtime(&file_stat.st_mtime);
printf(" 修改时间: %04d-%02d-%02d %02d:%02d:%02d",
mtime_tm->tm_year + 1900, mtime_tm->tm_mon + 1, mtime_tm->tm_mday,
mtime_tm->tm_hour, mtime_tm->tm_min, mtime_tm->tm_sec);
#ifdef st_mtim
printf(".%06ld", (long)file_stat.st_mtim.tv_nsec / 1000);
#endif
printf("\n");
// 显示状态改变时间
struct tm* ctime_tm = localtime(&file_stat.st_ctime);
printf(" 状态改变时间: %04d-%02d-%02d %02d:%02d:%02d\n",
ctime_tm->tm_year + 1900, ctime_tm->tm_mon + 1, ctime_tm->tm_mday,
ctime_tm->tm_hour, ctime_tm->tm_min, ctime_tm->tm_sec);
}
// 将timeval转换为可读格式
void print_timeval(const char* label, const struct timeval* tv) {
struct tm* tm_info = localtime(&tv->tv_sec);
printf("%s: %04d-%02d-%02d %02d:%02d:%02d.%06ld\n",
label,
tm_info->tm_year + 1900, tm_info->tm_mon + 1, tm_info->tm_mday,
tm_info->tm_hour, tm_info->tm_min, tm_info->tm_sec,
(long)tv->tv_usec);
}
int main() {
printf("=== 基础utimes使用示例 ===\n");
const char* test_file = "utimes_test.txt";
// 创建测试文件
if (create_test_file(test_file, "这是utimes测试文件的内容\n") == -1) {
exit(EXIT_FAILURE);
}
// 显示初始时间
printf("\n1. 初始文件时间:\n");
show_detailed_file_times(test_file);
// 获取当前时间
struct timeval current_time;
if (gettimeofday(¤t_time, NULL) == -1) {
perror("获取当前时间失败");
unlink(test_file);
exit(EXIT_FAILURE);
}
printf("\n当前系统时间: ");
print_timeval("", ¤t_time);
// 示例1: 使用utimes设置特定时间(微秒级精度)
printf("\n2. 使用utimes设置特定时间(微秒级精度):\n");
struct timeval new_times[2];
// 设置访问时间为当前时间减去1小时,微秒设为123456
new_times[0].tv_sec = current_time.tv_sec - 3600; // 1小时前
new_times[0].tv_usec = 123456; // 微秒部分
// 设置修改时间为当前时间减去2小时,微秒设为654321
new_times[1].tv_sec = current_time.tv_sec - 7200; // 2小时前
new_times[1].tv_usec = 654321; // 微秒部分
printf("设置新的时间戳:\n");
print_timeval(" 访问时间", &new_times[0]);
print_timeval(" 修改时间", &new_times[1]);
if (utimes(test_file, new_times) == 0) {
printf("✓ 时间修改成功\n");
} else {
printf("✗ 时间修改失败: %s\n", strerror(errno));
}
// 显示修改后的时间
printf("\n修改后的文件时间:\n");
show_detailed_file_times(test_file);
// 示例2: 使用utimes设置为当前时间
printf("\n3. 使用utimes设置为当前时间:\n");
if (utimes(test_file, NULL) == 0) {
printf("✓ 时间设置为当前时间成功\n");
} else {
printf("✗ 时间设置失败: %s\n", strerror(errno));
}
// 显示当前时间的文件
printf("\n设置为当前时间后的文件时间:\n");
show_detailed_file_times(test_file);
// 示例3: 设置未来时间(包含微秒)
printf("\n4. 设置未来时间(包含微秒):\n");
struct timeval future_times[2];
// 设置为1天后的时间,使用特定的微秒值
future_times[0].tv_sec = current_time.tv_sec + 86400; // 1天后
future_times[0].tv_usec = 987654; // 特定微秒值
future_times[1].tv_sec = current_time.tv_sec + 172800; // 2天后
future_times[1].tv_usec = 456789; // 特定微秒值
printf("设置未来时间戳:\n");
print_timeval(" 访问时间", &future_times[0]);
print_timeval(" 修改时间", &future_times[1]);
if (utimes(test_file, future_times) == 0) {
printf("✓ 未来时间设置成功\n");
} else {
printf("✗ 未来时间设置失败: %s\n", strerror(errno));
}
show_detailed_file_times(test_file);
// 验证微秒精度
printf("\n5. 验证微秒精度:\n");
struct stat verify_stat;
if (stat(test_file, &verify_stat) == 0) {
printf("验证微秒精度:\n");
#ifdef st_atim
printf(" 访问时间微秒: %06ld (期望: 987654)\n",
(long)verify_stat.st_atim.tv_nsec / 1000);
#endif
#ifdef st_mtim
printf(" 修改时间微秒: %06ld (期望: 456789)\n",
(long)verify_stat.st_mtim.tv_nsec / 1000);
#endif
}
// 清理测试文件
unlink(test_file);
printf("\n=== 基础utimes演示完成 ===\n");
return 0;
}
示例2:utimes与文件操作的时间关系 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <errno.h>
// 显示完整的timeval信息
void show_complete_times(const char* filename) {
struct stat file_stat;
if (stat(filename, &file_stat) == -1) {
perror("获取文件状态失败");
return;
}
printf("文件 %s 的完整时间信息:\n", filename);
// 访问时间
struct tm* atime_tm = localtime(&file_stat.st_atime);
printf(" 访问时间 (atime): %ld.%06ld - %04d-%02d-%02d %02d:%02d:%02d",
(long)file_stat.st_atime,
#ifdef st_atim
(long)file_stat.st_atim.tv_nsec / 1000,
#else
0L,
#endif
atime_tm->tm_year + 1900, atime_tm->tm_mon + 1, atime_tm->tm_mday,
atime_tm->tm_hour, atime_tm->tm_min, atime_tm->tm_sec);
printf("\n");
// 修改时间
struct tm* mtime_tm = localtime(&file_stat.st_mtime);
printf(" 修改时间 (mtime): %ld.%06ld - %04d-%02d-%02d %02d:%02d:%02d",
(long)file_stat.st_mtime,
#ifdef st_mtim
(long)file_stat.st_mtim.tv_nsec / 1000,
#else
0L,
#endif
mtime_tm->tm_year + 1900, mtime_tm->tm_mon + 1, mtime_tm->tm_mday,
mtime_tm->tm_hour, mtime_tm->tm_min, mtime_tm->tm_sec);
printf("\n");
// 状态改变时间
struct tm* ctime_tm = localtime(&file_stat.st_ctime);
printf(" 状态改变时间 (ctime): %ld - %04d-%02d-%02d %02d:%02d:%02d\n",
(long)file_stat.st_ctime,
ctime_tm->tm_year + 1900, ctime_tm->tm_mon + 1, ctime_tm->tm_mday,
ctime_tm->tm_hour, ctime_tm->tm_min, ctime_tm->tm_sec);
}
// 读取文件内容
int read_file_content(const char* filename) {
int fd = open(filename, O_RDONLY);
if (fd == -1) {
perror("打开文件失败");
return -1;
}
char buffer[256];
ssize_t bytes_read = read(fd, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0';
printf("读取内容: %s", buffer);
}
close(fd);
return 0;
}
// 写入文件内容
int write_file_content(const char* filename, const char* content) {
int fd = open(filename, O_WRONLY | O_APPEND);
if (fd == -1) {
perror("打开文件失败");
return -1;
}
if (write(fd, content, strlen(content)) == -1) {
perror("写入文件失败");
close(fd);
return -1;
}
close(fd);
return 0;
}
int main() {
printf("=== utimes与文件操作时间关系示例 ===\n");
const char* test_file = "time_relationship_test.txt";
// 创建测试文件
int fd = open(test_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
exit(EXIT_FAILURE);
}
write(fd, "初始内容\n", 9);
close(fd);
printf("1. 文件创建后的初始时间:\n");
show_complete_times(test_file);
// 等待一段时间(确保时间有变化)
printf("\n等待2秒...\n");
sleep(2);
// 读取文件(应该更新访问时间)
printf("\n2. 读取文件后的时间:\n");
read_file_content(test_file);
show_complete_times(test_file);
// 再等待一段时间
printf("\n等待2秒...\n");
sleep(2);
// 写入文件(应该更新修改时间和状态改变时间)
printf("\n3. 写入文件后的时间:\n");
write_file_content(test_file, "追加内容\n");
show_complete_times(test_file);
// 使用utimes修改时间为历史时间(微秒级)
printf("\n4. 使用utimes修改时间为历史时间(微秒级):\n");
struct timeval historical_times[2];
struct timeval current_time;
gettimeofday(¤t_time, NULL);
// 设置为30天前的时间,包含特定微秒值
historical_times[0].tv_sec = current_time.tv_sec - 86400 * 30; // 30天前
historical_times[0].tv_usec = 111111; // 特定微秒值
historical_times[1].tv_sec = historical_times[0].tv_sec;
historical_times[1].tv_usec = 222222; // 不同的微秒值
printf("设置为30天前的时间:\n");
printf(" 目标访问时间: %ld.%06ld\n",
(long)historical_times[0].tv_sec, (long)historical_times[0].tv_usec);
printf(" 目标修改时间: %ld.%06ld\n",
(long)historical_times[1].tv_sec, (long)historical_times[1].tv_usec);
if (utimes(test_file, historical_times) == 0) {
printf("✓ 历史时间设置成功\n");
} else {
printf("✗ 历史时间设置失败: %s\n", strerror(errno));
}
show_complete_times(test_file);
// 再次读取文件,观察时间变化
printf("\n5. 再次读取文件后的时间:\n");
read_file_content(test_file);
show_complete_times(test_file);
// 再次写入文件
printf("\n6. 再次写入文件后的时间:\n");
write_file_content(test_file, "再次追加内容\n");
show_complete_times(test_file);
// 演示utimes对ctime的影响
printf("\n7. 演示utimes对ctime的影响:\n");
// 保存当前ctime
struct stat stat_before;
stat(test_file, &stat_before);
time_t ctime_before = stat_before.st_ctime;
printf("utimes操作前的ctime: %ld\n", (long)ctime_before);
// 执行utimes操作
struct timeval new_times[2];
new_times[0].tv_sec = current_time.tv_sec - 1000;
new_times[0].tv_usec = 333333;
new_times[1].tv_sec = current_time.tv_sec - 2000;
new_times[1].tv_usec = 444444;
if (utimes(test_file, new_times) == 0) {
printf("执行utimes操作成功\n");
}
// 检查ctime是否改变
struct stat stat_after;
stat(test_file, &stat_after);
time_t ctime_after = stat_after.st_ctime;
printf("utimes操作后的ctime: %ld\n", (long)ctime_after);
if (ctime_before == ctime_after) {
printf("✓ utimes操作不会改变ctime(在某些系统上)\n");
} else {
printf("ℹ utimes操作改变了ctime(系统相关行为)\n");
}
// 演示微秒级时间控制
printf("\n8. 微秒级时间控制演示:\n");
// 设置一系列具有不同微秒值的时间
struct {
long usec_offset;
const char* description;
} microsecond_tests[] = {
{0, "0微秒"},
{1, "1微秒"},
{999999, "999999微秒"},
{500000, "500000微秒"},
{123456, "123456微秒"}
};
int num_tests = sizeof(microsecond_tests) / sizeof(microsecond_tests[0]);
for (int i = 0; i < num_tests; i++) {
struct timeval test_times[2];
gettimeofday(¤t_time, NULL);
test_times[0].tv_sec = current_time.tv_sec;
test_times[0].tv_usec = microsecond_tests[i].usec_offset;
test_times[1].tv_sec = current_time.tv_sec;
test_times[1].tv_usec = microsecond_tests[i].usec_offset;
if (utimes(test_file, test_times) == 0) {
printf(" 设置时间 %s: 成功\n", microsecond_tests[i].description);
// 验证设置的时间
struct stat verify_stat;
if (stat(test_file, &verify_stat) == 0) {
#ifdef st_atim
long actual_usec = verify_stat.st_atim.tv_nsec / 1000;
printf(" 实际微秒值: %06ld\n", actual_usec);
#endif
}
} else {
printf(" 设置时间 %s: 失败 (%s)\n",
microsecond_tests[i].description, strerror(errno));
}
}
// 清理
unlink(test_file);
printf("\n=== 时间关系演示完成 ===\n");
return 0;
}
示例3:批量文件时间管理(utimes版本) 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#define MAX_FILES 100
// 文件信息结构
typedef struct {
char filename[256];
struct timeval atime;
struct timeval mtime;
off_t size;
} file_info_t;
// 创建测试文件集合
int create_test_files(const char* directory) {
// 创建目录
if (mkdir(directory, 0755) == -1 && errno != EEXIST) {
perror("创建目录失败");
return -1;
}
// 创建测试文件
const char* filenames[] = {
"file1.txt", "file2.txt", "file3.txt",
"document.pdf", "image.jpg", "data.csv",
"script.sh", "config.xml", "readme.md"
};
const char* contents[] = {
"文件1的内容", "文件2的内容", "文件3的内容",
"PDF文档内容", "图片文件内容", "CSV数据内容",
"#!/bin/sh\necho '脚本文件'", "<?xml version='1.0'?><root>XML内容</root>", "# README\n说明文档"
};
int num_files = sizeof(filenames) / sizeof(filenames[0]);
for (int i = 0; i < num_files; i++) {
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", directory, filenames[i]);
int fd = open(full_path, O_CREAT | O_WRONLY | O_TRUNC,
(i == 6) ? 0755 : 0644); // script.sh设置可执行权限
if (fd != -1) {
write(fd, contents[i], strlen(contents[i]));
close(fd);
printf("创建文件: %s\n", full_path);
}
}
return 0;
}
// 获取目录中所有文件信息
int get_directory_files(const char* directory, file_info_t files[], int max_files) {
DIR* dir = opendir(directory);
if (dir == NULL) {
perror("打开目录失败");
return -1;
}
int count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL && count < max_files) {
// 跳过.和..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// 构造完整路径
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", directory, entry->d_name);
// 获取文件状态
struct stat file_stat;
if (stat(full_path, &file_stat) == 0) {
strncpy(files[count].filename, full_path, sizeof(files[count].filename) - 1);
// 转换时间格式
files[count].atime.tv_sec = file_stat.st_atime;
files[count].mtime.tv_sec = file_stat.st_mtime;
#ifdef st_atim
files[count].atime.tv_usec = file_stat.st_atim.tv_nsec / 1000;
files[count].mtime.tv_usec = file_stat.st_mtim.tv_nsec / 1000;
#else
files[count].atime.tv_usec = 0;
files[count].mtime.tv_usec = 0;
#endif
files[count].size = file_stat.st_size;
count++;
}
}
closedir(dir);
return count;
}
// 显示文件列表(包含微秒信息)
void show_file_list_with_microseconds(file_info_t files[], int count) {
printf("文件列表 (%d 个文件):\n", count);
printf("%-30s %-25s %-25s %10s\n", "文件名", "修改时间", "访问时间", "大小");
printf("%-30s %-25s %-25s %10s\n", "------", "--------", "--------", "----");
for (int i = 0; i < count; i++) {
struct tm* mtime_tm = localtime(&files[i].mtime.tv_sec);
struct tm* atime_tm = localtime(&files[i].atime.tv_sec);
char mtime_str[32], atime_str[32];
snprintf(mtime_str, sizeof(mtime_str), "%02d-%02d %02d:%02d:%02d.%06ld",
mtime_tm->tm_mon + 1, mtime_tm->tm_mday,
mtime_tm->tm_hour, mtime_tm->tm_min, mtime_tm->tm_sec,
(long)files[i].mtime.tv_usec);
snprintf(atime_str, sizeof(atime_str), "%02d-%02d %02d:%02d:%02d.%06ld",
atime_tm->tm_mon + 1, atime_tm->tm_mday,
atime_tm->tm_hour, atime_tm->tm_min, atime_tm->tm_sec,
(long)files[i].atime.tv_usec);
// 提取文件名(去掉路径)
char* filename = strrchr(files[i].filename, '/');
if (filename) filename++;
else filename = files[i].filename;
printf("%-30s %-25s %-25s %10ld\n",
filename, mtime_str, atime_str, (long)files[i].size);
}
printf("\n");
}
// 批量更新文件时间(微秒级精度)
int batch_update_times_with_microseconds(const char* directory, struct timeval new_time) {
DIR* dir = opendir(directory);
if (dir == NULL) {
perror("打开目录失败");
return -1;
}
int updated_count = 0;
struct dirent* entry;
struct timeval new_times[2];
new_times[0] = new_time; // 访问时间
new_times[1] = new_time; // 修改时间
while ((entry = readdir(dir)) != NULL) {
// 跳过.和..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// 构造完整路径
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", directory, entry->d_name);
// 更新文件时间
if (utimes(full_path, new_times) == 0) {
printf("更新文件时间: %s\n", full_path);
updated_count++;
} else {
printf("更新文件时间失败: %s (%s)\n", full_path, strerror(errno));
}
}
closedir(dir);
return updated_count;
}
// 根据文件扩展名设置不同时间(包含微秒)
int update_times_by_extension_with_microseconds(const char* directory) {
DIR* dir = opendir(directory);
if (dir == NULL) {
perror("打开目录失败");
return -1;
}
struct timeval now;
gettimeofday(&now, NULL);
int updated_count = 0;
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
// 跳过.和..
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
continue;
}
// 构造完整路径
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", directory, entry->d_name);
// 根据扩展名设置不同时间(包含微秒)
struct timeval ext_times[2];
char* dot = strrchr(entry->d_name, '.');
if (dot) {
if (strcmp(dot, ".txt") == 0) {
ext_times[0].tv_sec = now.tv_sec - 3600; // 1小时前
ext_times[0].tv_usec = 111111; // 特定微秒
ext_times[1].tv_sec = now.tv_sec - 7200; // 2小时前
ext_times[1].tv_usec = 222222; // 特定微秒
} else if (strcmp(dot, ".pdf") == 0) {
ext_times[0].tv_sec = now.tv_sec - 86400; // 1天前
ext_times[0].tv_usec = 333333; // 特定微秒
ext_times[1].tv_sec = now.tv_sec - 172800; // 2天前
ext_times[1].tv_usec = 444444; // 特定微秒
} else if (strcmp(dot, ".jpg") == 0) {
ext_times[0].tv_sec = now.tv_sec - 604800; // 1周前
ext_times[0].tv_usec = 555555; // 特定微秒
ext_times[1].tv_sec = now.tv_sec - 1209600; // 2周前
ext_times[1].tv_usec = 666666; // 特定微秒
} else if (strcmp(dot, ".sh") == 0) {
ext_times[0].tv_sec = now.tv_sec - 1800; // 30分钟前
ext_times[0].tv_usec = 777777; // 特定微秒
ext_times[1].tv_sec = now.tv_sec - 3600; // 1小时前
ext_times[1].tv_usec = 888888; // 特定微秒
} else {
ext_times[0] = now;
ext_times[1] = now;
}
} else {
ext_times[0] = now;
ext_times[1] = now;
}
// 更新文件时间
if (utimes(full_path, ext_times) == 0) {
printf("根据扩展名更新时间: %s\n", full_path);
updated_count++;
}
}
closedir(dir);
return updated_count;
}
// 高精度时间戳验证
void verify_microsecond_precision(file_info_t files[], int count) {
printf("=== 微秒级精度验证 ===\n");
for (int i = 0; i < count && i < 5; i++) {
struct stat file_stat;
if (stat(files[i].filename, &file_stat) == 0) {
printf("文件: %s\n", strrchr(files[i].filename, '/') ?
strrchr(files[i].filename, '/') + 1 : files[i].filename);
#ifdef st_atim
long expected_usec = files[i].atime.tv_usec;
long actual_usec = file_stat.st_atim.tv_nsec / 1000;
printf(" 访问时间微秒 - 期望: %06ld, 实际: %06ld, %s\n",
expected_usec, actual_usec,
(expected_usec == actual_usec) ? "✓ 匹配" : "✗ 不匹配");
#endif
#ifdef st_mtim
long expected_musec = files[i].mtime.tv_usec;
long actual_musec = file_stat.st_mtim.tv_nsec / 1000;
printf(" 修改时间微秒 - 期望: %06ld, 实际: %06ld, %s\n",
expected_musec, actual_musec,
(expected_musec == actual_musec) ? "✓ 匹配" : "✗ 不匹配");
#endif
printf("\n");
}
}
}
int main() {
printf("=== 批量文件时间管理(utimes版本)示例 ===\n");
const char* test_dir = "batch_time_test_utimes";
// 创建测试文件集合
printf("1. 创建测试文件集合:\n");
if (create_test_files(test_dir) == -1) {
exit(EXIT_FAILURE);
}
// 获取并显示初始文件列表
file_info_t files[MAX_FILES];
int file_count = get_directory_files(test_dir, files, MAX_FILES);
printf("\n2. 初始文件列表:\n");
show_file_list_with_microseconds(files, file_count);
// 批量更新所有文件时间为当前时间(微秒级)
printf("3. 批量更新所有文件时间为当前时间(微秒级):\n");
struct timeval current_time;
gettimeofday(¤t_time, NULL);
// 设置特定的微秒值来验证精度
current_time.tv_usec = 999999; // 设置为最大微秒值
int updated = batch_update_times_with_microseconds(test_dir, current_time);
printf("成功更新 %d 个文件的时间\n", updated);
// 显示更新后的文件列表
file_count = get_directory_files(test_dir, files, MAX_FILES);
printf("\n4. 更新后的文件列表:\n");
show_file_list_with_microseconds(files, file_count);
// 验证微秒级精度
verify_microsecond_precision(files, file_count);
// 等待一段时间
printf("等待3秒...\n");
sleep(3);
// 根据文件扩展名设置不同时间(包含微秒)
printf("\n5. 根据文件扩展名设置不同时间(包含微秒):\n");
updated = update_times_by_extension_with_microseconds(test_dir);
printf("成功更新 %d 个文件的时间\n", updated);
// 显示最终文件列表
file_count = get_directory_files(test_dir, files, MAX_FILES);
printf("\n6. 最终文件列表:\n");
show_file_list_with_microseconds(files, file_count);
// 演示时间排序(按修改时间)
printf("\n7. 按修改时间排序的文件列表:\n");
// 简单冒泡排序
for (int i = 0; i < file_count - 1; i++) {
for (int j = 0; j < file_count - 1 - i; j++) {
// 比较秒数
if (files[j].mtime.tv_sec > files[j + 1].mtime.tv_sec) {
file_info_t temp = files[j];
files[j] = files[j + 1];
files[j + 1] = temp;
} else if (files[j].mtime.tv_sec == files[j + 1].mtime.tv_sec) {
// 秒数相同时比较微秒数
if (files[j].mtime.tv_usec > files[j + 1].mtime.tv_usec) {
file_info_t temp = files[j];
files[j] = files[j + 1];
files[j + 1] = temp;
}
}
}
}
printf("按修改时间排序 (从旧到新):\n");
show_file_list_with_microseconds(files, file_count);
// 性能测试
printf("\n8. 性能测试:\n");
clock_t start_time = clock();
// 快速连续设置时间1000次
struct timeval test_time;
gettimeofday(&test_time, NULL);
for (int i = 0; i < 1000; i++) {
test_time.tv_usec = i % 1000000; // 变化微秒值
utimes(files[0].filename, (i % 2 == 0) ? &test_time : NULL);
}
clock_t end_time = clock();
double elapsed_time = ((double)(end_time - start_time)) / CLOCKS_PER_SEC;
printf("1000次utimes调用耗时: %.6f 秒\n", elapsed_time);
printf("平均每次调用: %.6f 毫秒\n", (elapsed_time * 1000) / 1000);
// 清理测试文件
DIR* dir = opendir(test_dir);
if (dir) {
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (strcmp(entry->d_name, ".") != 0 && strcmp(entry->d_name, "..") != 0) {
char full_path[512];
snprintf(full_path, sizeof(full_path), "%s/%s", test_dir, entry->d_name);
unlink(full_path);
}
}
closedir(dir);
rmdir(test_dir);
}
printf("\n=== 批量文件时间管理演示完成 ===\n");
return 0;
}
示例4:高级时间管理与备份应用(utimes版本) 链接到标题
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <errno.h>
#include <utime.h>
// 备份记录结构(包含微秒级时间)
typedef struct {
char original_path[512];
char backup_path[512];
struct timeval backup_time;
struct timeval original_atime;
struct timeval original_mtime;
} backup_record_t;
#define MAX_BACKUPS 1000
// 全局备份记录数组
static backup_record_t backup_records[MAX_BACKUPS];
static int backup_count = 0;
// 复制文件(保持时间戳,包括微秒)
int copy_file_with_timestamps_microseconds(const char* src, const char* dst) {
// 获取源文件状态
struct stat src_stat;
if (stat(src, &src_stat) == -1) {
perror("获取源文件状态失败");
return -1;
}
// 打开源文件
int src_fd = open(src, O_RDONLY);
if (src_fd == -1) {
perror("打开源文件失败");
return -1;
}
// 创建目标文件
int dst_fd = open(dst, O_CREAT | O_WRONLY | O_TRUNC, src_stat.st_mode);
if (dst_fd == -1) {
perror("创建目标文件失败");
close(src_fd);
return -1;
}
// 复制文件内容
char buffer[8192];
ssize_t bytes_read, bytes_written;
while ((bytes_read = read(src_fd, buffer, sizeof(buffer))) > 0) {
bytes_written = write(dst_fd, buffer, bytes_read);
if (bytes_written != bytes_read) {
perror("写入目标文件失败");
close(src_fd);
close(dst_fd);
return -1;
}
}
close(src_fd);
close(dst_fd);
// 设置目标文件的时间戳与源文件相同(包括微秒)
struct timeval timestamps[2];
// 访问时间
timestamps[0].tv_sec = src_stat.st_atime;
#ifdef st_atim
timestamps[0].tv_usec = src_stat.st_atim.tv_nsec / 1000;
#else
timestamps[0].tv_usec = 0;
#endif
// 修改时间
timestamps[1].tv_sec = src_stat.st_mtime;
#ifdef st_mtim
timestamps[1].tv_usec = src_stat.st_mtim.tv_nsec / 1000;
#else
timestamps[1].tv_usec = 0;
#endif
if (utimes(dst, timestamps) == -1) {
perror("设置目标文件时间戳失败");
return -1;
}
printf("复制文件: %s -> %s (保持时间戳,包括微秒)\n", src, dst);
return 0;
}
// 创建备份(微秒级精度)
int create_backup_microseconds(const char* original_path, const char* backup_dir) {
if (backup_count >= MAX_BACKUPS) {
fprintf(stderr, "备份记录已满\n");
return -1;
}
// 获取原始文件状态
struct stat original_stat;
if (stat(original_path, &original_stat) == -1) {
perror("获取原始文件状态失败");
return -1;
}
// 生成备份文件名(包含时间戳)
struct timeval now;
gettimeofday(&now, NULL);
char backup_filename[512];
char* filename = strrchr(original_path, '/');
if (filename) filename++;
else filename = (char*)original_path;
snprintf(backup_filename, sizeof(backup_filename),
"%s/%s.%ld.%06ld", backup_dir, filename,
(long)now.tv_sec, (long)now.tv_usec);
// 复制文件
if (copy_file_with_timestamps_microseconds(original_path, backup_filename) == -1) {
return -1;
}
// 记录备份信息
strncpy(backup_records[backup_count].original_path, original_path,
sizeof(backup_records[backup_count].original_path) - 1);
strncpy(backup_records[backup_count].backup_path, backup_filename,
sizeof(backup_records[backup_count].backup_path) - 1);
backup_records[backup_count].backup_time = now;
// 原始时间戳(包括微秒)
backup_records[backup_count].original_atime.tv_sec = original_stat.st_atime;
backup_records[backup_count].original_mtime.tv_sec = original_stat.st_mtime;
#ifdef st_atim
backup_records[backup_count].original_atime.tv_usec = original_stat.st_atim.tv_nsec / 1000;
#else
backup_records[backup_count].original_atime.tv_usec = 0;
#endif
#ifdef st_mtim
backup_records[backup_count].original_mtime.tv_usec = original_stat.st_mtim.tv_nsec / 1000;
#else
backup_records[backup_count].original_mtime.tv_usec = 0;
#endif
backup_count++;
printf("创建备份: %s -> %s\n", original_path, backup_filename);
return 0;
}
// 恢复备份(保持微秒级精度)
int restore_backup_microseconds(int backup_index) {
if (backup_index < 0 || backup_index >= backup_count) {
fprintf(stderr, "无效的备份索引\n");
return -1;
}
backup_record_t* record = &backup_records[backup_index];
// 复制备份文件到原位置
if (copy_file_with_timestamps_microseconds(record->backup_path, record->original_path) == -1) {
return -1;
}
// 恢复原始时间戳(包括微秒)
struct timeval original_times[2];
original_times[0] = record->original_atime;
original_times[1] = record->original_mtime;
if (utimes(record->original_path, original_times) == -1) {
perror("恢复原始时间戳失败");
return -1;
}
printf("恢复备份: %s <- %s\n", record->original_path, record->backup_path);
return 0;
}
// 显示备份列表(包含微秒信息)
void show_backup_list_with_microseconds() {
if (backup_count == 0) {
printf("没有备份记录\n");
return;
}
printf("备份列表 (%d 个备份):\n", backup_count);
printf("%-3s %-30s %-30s %-25s\n", "ID", "原始文件", "备份文件", "备份时间");
printf("%-3s %-30s %-30s %-25s\n", "--", "--------", "--------", "--------");
for (int i = 0; i < backup_count; i++) {
char* orig_name = strrchr(backup_records[i].original_path, '/');
if (orig_name) orig_name++;
else orig_name = backup_records[i].original_path;
char* backup_name = strrchr(backup_records[i].backup_path, '/');
if (backup_name) backup_name++;
else backup_name = backup_records[i].backup_path;
struct tm* time_tm = localtime(&backup_records[i].backup_time.tv_sec);
char time_str[32];
snprintf(time_str, sizeof(time_str), "%02d-%02d %02d:%02d:%02d.%06ld",
time_tm->tm_mon + 1, time_tm->tm_mday,
time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec,
(long)backup_records[i].backup_time.tv_usec);
printf("%-3d %-30s %-30s %-25s\n", i, orig_name, backup_name, time_str);
}
printf("\n");
}
// 创建测试文件(包含微秒级时间戳)
int create_test_file_with_microseconds(const char* filename, const char* content, struct timeval timestamp) {
int fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd == -1) {
perror("创建测试文件失败");
return -1;
}
if (write(fd, content, strlen(content)) == -1) {
perror("写入测试文件失败");
close(fd);
return -1;
}
close(fd);
// 设置特定时间戳(包括微秒)
struct timeval file_times[2];
file_times[0] = timestamp; // 访问时间
file_times[1] = timestamp; // 修改时间
if (utimes(filename, file_times) == -1) {
perror("设置文件时间戳失败");
return -1;
}
struct tm* time_tm = localtime(×tamp.tv_sec);
printf("创建测试文件: %s (时间戳: %04d-%02d-%02d %02d:%02d:%02d.%06ld)\n",
filename,
time_tm->tm_year + 1900, time_tm->tm_mon + 1, time_tm->tm_mday,
time_tm->tm_hour, time_tm->tm_min, time_tm->tm_sec,
(long)timestamp.tv_usec);
return 0;
}
// 模拟文件修改场景(微秒级)
void simulate_file_modifications_microseconds(const char* filename) {
printf("\n模拟文件修改场景(微秒级精度):\n");
// 读取文件(更新访问时间)
int fd = open(filename, O_RDONLY);
if (fd != -1) {
char buffer[256];
read(fd, buffer, sizeof(buffer));
close(fd);
printf("读取文件 %s (更新访问时间)\n", filename);
}
// 等待
usleep(500000); // 0.5秒
// 写入文件(更新修改时间)
fd = open(filename, O_WRONLY | O_APPEND);
if (fd != -1) {
write(fd, "追加内容\n", 9);
close(fd);
printf("写入文件 %s (更新修改时间)\n", filename);
}
// 显示修改后的时间(包括微秒)
struct stat file_stat;
if (stat(filename, &file_stat) == 0) {
printf("修改后的时间:\n");
struct tm* atime_tm = localtime(&file_stat.st_atime);
struct tm* mtime_tm = localtime(&file_stat.st_mtime);
printf(" 访问时间: %04d-%02d-%02d %02d:%02d:%02d",
atime_tm->tm_year + 1900, atime_tm->tm_mon + 1, atime_tm->tm_mday,
atime_tm->tm_hour, atime_tm->tm_min, atime_tm->tm_sec);
#ifdef st_atim
printf(".%06ld", (long)file_stat.st_atim.tv_nsec / 1000);
#endif
printf("\n");
printf(" 修改时间: %04d-%02d-%02d %02d:%02d:%02d",
mtime_tm->tm_year + 1900, mtime_tm->tm_mon + 1, mtime_tm->tm_mday,
mtime_tm->tm_hour, mtime_tm->tm_min, mtime_tm->tm_sec);
#ifdef st_mtim
printf(".%06ld", (long)file_stat.st_mtim.tv_nsec / 1000);
#endif
printf("\n");
}
}
// 时间精度对比测试
void time_precision_comparison_test() {
printf("=== 时间精度对比测试 ===\n");
const char* test_file = "precision_test.txt";
// 创建测试文件
int fd = open(test_file, O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd != -1) {
write(fd, "精度测试", 8);
close(fd);
}
// 测试utimes的微秒精度
printf("1. utimes微秒级精度测试:\n");
for (int i = 0; i < 10; i++) {
struct timeval test_times[2];
struct timeval now;
gettimeofday(&now, NULL);
test_times[0].tv_sec = now.tv_sec;
test_times[0].tv_usec = i * 100000; // 0, 100000, 200000, ...
test_times[1] = test_times[0];
if (utimes(test_file, test_times) == 0) {
struct stat verify_stat;
if (stat(test_file, &verify_stat) == 0) {
#ifdef st_atim
long actual_usec = verify_stat.st_atim.tv_nsec / 1000;
printf(" 设置微秒: %06ld, 实际微秒: %06ld, %s\n",
(long)test_times[0].tv_usec, actual_usec,
(test_times[0].tv_usec == actual_usec) ? "✓ 精确" : "✗ 有偏差");
#endif
}
}
}
// 测试不同时间函数的精度
printf("\n2. 不同时间函数精度对比:\n");
struct timeval start_time, end_time;
// utimes测试
gettimeofday(&start_time, NULL);
struct timeval test_time = start_time;
test_time.tv_usec = 123456;
utimes(test_file, &test_time);
gettimeofday(&end_time, NULL);
double utimes_time = (end_time.tv_sec - start_time.tv_sec) * 1000000.0 +
(end_time.tv_usec - start_time.tv_usec);
printf(" utimes耗时: %.2f 微秒\n", utimes_time);
// utime测试
struct utimbuf utime_buf;
utime_buf.actime = start_time.tv_sec;
utime_buf.modtime = start_time.tv_sec;
gettimeofday(&start_time, NULL);
utime(test_file, &utime_buf);
gettimeofday(&end_time, NULL);
double utime_time = (end_time.tv_sec - start_time.tv_sec) * 1000000.0 +
(end_time.tv_usec - start_time.tv_usec);
printf(" utime耗时: %.2f 微秒\n", utime_time);
printf(" 精度提升: utimes支持微秒级,utime只支持秒级\n");
unlink(test_file);
printf("=== 精度对比测试完成 ===\n\n");
}
int main() {
printf("=== 高级时间管理与备份应用(utimes版本)示例 ===\n");
const char* backup_dir = "microsecond_backups";
const char* test_file = "precise_file.txt";
// 时间精度对比测试
time_precision_comparison_test();
// 创建备份目录
if (mkdir(backup_dir, 0755) == -1 && errno != EEXIST) {
perror("创建备份目录失败");
exit(EXIT_FAILURE);
}
// 创建测试文件(设置为历史时间,包含微秒)
struct timeval historical_time;
gettimeofday(&historical_time, NULL);
historical_time.tv_sec -= 86400 * 30; // 30天前
historical_time.tv_usec = 987654; // 特定微秒值
if (create_test_file_with_microseconds(test_file, "重要内容\n", historical_time) == -1) {
exit(EXIT_FAILURE);
}
// 显示初始文件时间(包括微秒)
struct stat initial_stat;
stat(test_file, &initial_stat);
printf("\n初始文件时间:\n");
printf(" 访问时间: %ld.%06ld\n",
(long)initial_stat.st_atime,
#ifdef st_atim
(long)initial_stat.st_atim.tv_nsec / 1000
#else
0L
#endif
);
printf(" 修改时间: %ld.%06ld\n",
(long)initial_stat.st_mtime,
#ifdef st_mtim
(long)initial_stat.st_mtim.tv_nsec / 1000
#else
0L
#endif
);
// 创建第一次备份
printf("\n1. 创建第一次备份:\n");
if (create_backup_microseconds(test_file, backup_dir) == -1) {
exit(EXIT_FAILURE);
}
show_backup_list_with_microseconds();
// 模拟文件修改
simulate_file_modifications_microseconds(test_file);
// 等待一段时间
usleep(500000); // 0.5秒
// 创建第二次备份
printf("\n2. 创建第二次备份:\n");
if (create_backup_microseconds(test_file, backup_dir) == -1) {
exit(EXIT_FAILURE);
}
show_backup_list_with_microseconds();
// 再次修改文件
printf("\n3. 再次修改文件:\n");
int fd = open(test_file, O_WRONLY | O_APPEND);
if (fd != -1) {
write(fd, "更多重要内容\n", 13);
close(fd);
printf("再次修改文件 %s\n", test_file);
}
// 显示当前文件状态
struct stat current_stat;
stat(test_file, ¤t_stat);
printf("当前文件时间:\n");
printf(" 访问时间: %ld.%06ld\n",
(long)current_stat.st_atime,
#ifdef st_atim
(long)current_stat.st_atim.tv_nsec / 1000
#else
0L
#endif
);
printf(" 修改时间: %ld.%06ld\n",
(long)current_stat.st_mtime,
#ifdef st_mtim
(long)current_stat.st_mtim.tv_nsec / 1000
#else
0L
#endif
);
// 创建第三次备份
printf("\n4. 创建第三次备份:\n");
if (create_backup_microseconds(test_file, backup_dir) == -1) {
exit(EXIT_FAILURE);
}
show_backup_list_with_microseconds();
// 演示备份恢复(保持微秒级精度)
printf("\n5. 演示备份恢复(保持微秒级精度):\n");
// 恢复到第一次备份
printf("恢复到第一次备份:\n");
if (restore_backup_microseconds(0) == 0) {
struct stat restored_stat;
stat(test_file, &restored_stat);
printf("恢复后的文件时间:\n");
printf(" 访问时间: %ld.%06ld\n",
(long)restored_stat.st_atime,
#ifdef st_atim
(long)restored_stat.st_atim.tv_nsec / 1000
#else
0L
#endif
);
printf(" 修改时间: %ld.%06ld\n",
(long)restored_stat.st_mtime,
#ifdef st_mtim
(long)restored_stat.st_mtim.tv_nsec / 1000
#else
0L
#endif
);
}
// 恢复到第二次备份
printf("\n恢复到第二次备份:\n");
if (restore_backup_microseconds(1) == 0) {
struct stat restored_stat;
stat(test_file, &restored_stat);
printf("恢复后的文件时间:\n");
printf(" 访问时间: %ld.%06ld\n",
(long)restored_stat.st_atime,
#ifdef st_atim
(long)restored_stat.st_atim.tv_nsec / 1000
#else
0L
#endif
);
printf(" 修改时间: %ld.%06ld\n",
(long)restored_stat.st_mtime,
#ifdef st_mtim
(long)restored_stat.st_mtim.tv_nsec / 1000
#else
0L
#endif
);
}
// 演示时间戳验证(微秒级)
printf("\n6. 时间戳验证(微秒级):\n");
for (int i = 0; i < backup_count; i++) {
struct stat backup_stat;
if (stat(backup_records[i].backup_path, &backup_stat) == 0) {
printf("备份 %d:\n", i);
printf(" 备份文件修改时间: %ld.%06ld\n",
(long)backup_stat.st_mtime,
#ifdef st_mtim
(long)backup_stat.st_mtim.tv_nsec / 1000
#else
0L
#endif
);
printf(" 原始文件修改时间: %ld.%06ld\n",
(long)backup_records[i].original_mtime.tv_sec,
(long)backup_records[i].original_mtime.tv_usec);
long backup_usec =
#ifdef st_mtim
backup_stat.st_mtim.tv_nsec / 1000;
#else
0L;
#endif
if (backup_stat.st_mtime == backup_records[i].original_mtime.tv_sec &&
backup_usec == backup_records[i].original_mtime.tv_usec) {
printf(" ✓ 时间戳精确匹配(微秒级)\n");
} else {
printf(" ✗ 时间戳不匹配\n");
}
}
}
// 演示错误处理
printf("\n7. 错误处理演示:\n");
// 尝试恢复不存在的备份
printf("尝试恢复不存在的备份:\n");
if (restore_backup_microseconds(999) == -1) {
printf("✓ 正确处理了无效备份索引\n");
}
// 尝试备份不存在的文件
printf("尝试备份不存在的文件:\n");
if (create_backup_microseconds("nonexistent.txt", backup_dir) == -1) {
printf("✓ 正确处理了不存在的文件\n");
}
// 清理测试文件
printf("\n8. 清理测试文件:\n");
unlink(test_file);
// 清理备份文件
for (int i = 0; i < backup_count; i++) {
unlink(backup_records[i].backup_path);
printf("删除备份文件: %s\n", backup_records[i].backup_path);
}
rmdir(backup_dir);
printf("\n=== 高级时间管理与备份应用演示完成 ===\n");
return 0;
}
编译和运行 链接到标题
# 编译示例
gcc -o utimes_example1 utimes_example1.c
gcc -o utimes_example2 utimes_example2.c
gcc -o utimes_example3 utimes_example3.c
gcc -o utimes_example4 utimes_example4.c
# 运行示例
./utimes_example1
./utimes_example2
./utimes_example3
./utimes_example4
重要注意事项 链接到标题
- 时间精度: utimes支持微秒级精度,在支持的系统上可以精确到微秒
- 权限要求: 需要对文件有写权限或具有特殊权限(如CAP_FOWNER)
- ctime影响: utimes操作通常会更新文件的状态改变时间(ctime)
- 符号链接: utimes会跟随符号链接修改目标文件
- 目录时间: 可以修改目录的时间戳
- 原子性: utimes操作是原子的
- 错误处理: 必须检查返回值并适当处理错误
- 系统差异: 不同系统对微秒精度的支持可能有差异
最佳实践 链接到标题
- 使用安全包装函数: 对utimes进行封装以提供更好的错误处理
- 检查返回值: 始终检查函数返回值
- 权限检查: 在修改时间前检查必要的权限
- 时间验证: 验证设置的时间是否合理
- 批量操作: 在批量操作时考虑性能优化
- 错误日志: 记录时间修改操作的详细信息
- 备份策略: 在重要文件时间修改前考虑备份
- 精度要求: 根据应用需求选择合适的时间精度函数
通过这些示例,你可以理解utimes在文件时间管理方面的强大功能,它为Linux系统提供了微秒级精度的时间控制能力,特别适用于需要精确时间戳管理的备份、同步和测试等场景。