gettimeofday和settimeofday的用法及示例
gettimeofday 和 settimeofday 是用于获取和设置系统时间的系统调用,适用于需要高精度时间操作的场景。以下是它们的详细用法及示例:
1. gettimeofday
函数原型:
1 2 3
| #include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
|
参数说明:
- tv:指向
struct timeval 的指针,用于存储获取的时间(精确到微秒)。
- tz:时区信息,通常已废弃,设置为
NULL。
struct timeval 结构:
1 2 3 4
| struct timeval { time_t tv_sec; suseconds_t tv_usec; };
|
示例:获取当前时间
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| #include <sys/time.h> #include <stdio.h> #include <unistd.h>
int main() { struct timeval tv; if (gettimeofday(&tv, NULL) == 0) { printf("Seconds since Epoch: %ld\n", tv.tv_sec); printf("Microseconds: %ld\n", tv.tv_usec); } else { perror("gettimeofday failed"); } return 0; }
|
2. settimeofday
函数原型:
1 2 3
| #include <sys/time.h>
int settimeofday(const struct timeval *tv, const struct timezone *tz);
|
参数说明:
- tv:指向
struct timeval 的指针,包含要设置的时间。
- tz:时区信息,通常设置为
NULL。
注意事项:
- 需要 root 权限(如使用
sudo 运行程序)。
- 直接修改系统时间可能导致不可预见的后果(如日志混乱),需谨慎使用。
示例:设置系统时间
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 39
| #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #include <time.h>
int main() { struct timeval tv; struct tm tm_time;
tm_time.tm_year = 2023 - 1900; tm_time.tm_mon = 10 - 1; tm_time.tm_mday = 1; tm_time.tm_hour = 12; tm_time.tm_min = 0; tm_time.tm_sec = 0; tm_time.tm_isdst = -1;
time_t t = mktime(&tm_time); if (t == (time_t)-1) { perror("mktime failed"); exit(EXIT_FAILURE); }
tv.tv_sec = t; tv.tv_usec = 0;
if (settimeofday(&tv, NULL) == 0) { printf("System time updated successfully.\n"); } else { perror("settimeofday failed"); exit(EXIT_FAILURE); }
return 0; }
|
注意事项
时区处理:
mktime 默认将 struct tm 视为本地时间。若需使用 UTC 时间,可使用 timegm(非标准,需在 Linux 中定义 _BSD_SOURCE)或调整时区:1 2
| setenv("TZ", "UTC", 1); tzset();
|
处理完毕后恢复原时区。
权限问题:
- 运行
settimeofday 示例时需使用 sudo:
替代方案:
- 对于时间调整,推荐使用
ntp 服务或 adjtime 逐步校正。
- 新代码建议使用
clock_gettime 和 clock_settime(精度更高)。
通过上述示例和说明,可安全地使用 gettimeofday 获取时间,并在必要时用 settimeofday 设置系统时间。
Linux内核ktime_get_real_ts64函数用法及示例
ktime_get_real_ts64 是 Linux 内核中用于获取当前真实时间(即“墙上时间”,wall time)的高精度函数,返回的时间格式为 timespec64(64 位秒和纳秒)。以下是详细用法及示例:
1. 函数原型与头文件
1 2 3
| #include <linux/timekeeping.h>
void ktime_get_real_ts64(struct timespec64 *ts);
|
参数说明:
- ts:指向
struct timespec64 的指针,用于存储获取的时间。
struct timespec64 结构:
1 2 3 4
| struct timespec64 { time64_t tv_sec; long tv_nsec; };
|
2. 使用场景
- 在内核模块中获取高精度实时时间(如日志记录、性能分析)。
- 需要避免用户空间与内核空间上下文切换的开销时。
- 支持 64 位时间(避免 2038 年溢出问题)。
3. 示例代码
内核模块示例:获取并打印当前时间
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
| #include <linux/module.h> #include <linux/ktime.h> #include <linux/timekeeping.h>
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example of ktime_get_real_ts64");
static int __init example_init(void) { struct timespec64 ts;
ktime_get_real_ts64(&ts);
printk(KERN_INFO "Current time: %lld seconds, %ld nanoseconds\n", (long long)ts.tv_sec, ts.tv_nsec);
return 0; }
static void __exit example_exit(void) { printk(KERN_INFO "Module unloaded\n"); }
module_init(example_init); module_exit(example_exit);
|
4. 编译与加载
Makefile 示例:
1 2 3 4 5 6 7
| obj-m += example.o
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
操作步骤:
1 2 3 4 5 6 7 8 9 10 11
| make
sudo insmod example.ko
dmesg | tail
sudo rmmod example
|
5. 注意事项
内核上下文安全性:
ktime_get_real_ts64 可以在大多数上下文中调用(包括中断上下文),但需确认具体内核版本的实现。
- 避免在持有锁时调用,以减少延迟。
时间精度:
- 返回的时间精度为纳秒级,但实际精度取决于硬件和内核配置(如
CONFIG_HIGH_RES_TIMERS)。
时间来源:
- 返回的时间是系统实时时间,可能受 NTP 调整或
settimeofday 影响。
64 位时间:
- 使用
time64_t 避免 2038 年溢出问题(32 位系统兼容)。
6. 对比其他时间函数
| 函数 |
描述 |
精度 |
适用场景 |
ktime_get_real_ts64 |
获取实时时间(墙上时间) |
纳秒 |
需要 64 位时间的场景 |
ktime_get |
获取单调时间(不受 NTP 影响) |
纳秒 |
性能测量、超时计算 |
do_gettimeofday |
获取实时时间(旧接口) |
微秒 |
遗留代码兼容 |
current_kernel_time |
获取实时时间(旧接口) |
微秒 |
已废弃,不推荐使用 |
7. 时间格式转换
若需将 timespec64 转换为可读字符串,可使用 time64_to_tm 和 snprintf:
1 2 3 4 5 6 7 8 9
| #include <linux/time.h>
struct tm tm_time; time64_to_tm(ts.tv_sec, 0, &tm_time);
printk(KERN_INFO "UTC Time: %04d-%02d-%02d %02d:%02d:%02d\n", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
|
通过上述示例和说明,可以在内核模块中安全地使用 ktime_get_real_ts64 获取高精度实时时间。
Linux内核time64_to_tm 函数的用法及示例
time64_to_tm 是 Linux 内核中用于将 64 位时间戳(time64_t,自 1970-01-01 00:00:00 UTC 的秒数)转换为人类可读的分解时间(struct tm)的函数。以下是其详细用法及示例:
1. 函数原型与头文件
1 2 3
| #include <linux/time.h>
void time64_to_tm(time64_t totalsecs, int offset, struct tm *result);
|
参数说明:
- **
totalsecs**:待转换的 64 位时间戳(秒数)。
- **
offset**:时区偏移(单位:秒)。若设为 0,表示使用 UTC 时间;若需本地时间,需传入时区偏移(如东八区为 8*3600)。
- **
result**:指向 struct tm 的指针,用于存储转换后的分解时间。
struct tm 结构:
1 2 3 4 5 6 7 8 9 10 11
| struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; };
|
2. 使用场景
- 在内核模块中将时间戳转换为可读的年、月、日、时、分、秒。
- 生成日志时间戳、调试信息或处理时间相关逻辑。
3. 示例代码
内核模块示例:将时间戳转换为分解时间并打印
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
| #include <linux/module.h> #include <linux/time.h> #include <linux/timekeeping.h>
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Example of time64_to_tm");
static int __init example_init(void) { struct timespec64 ts; struct tm tm_time;
ktime_get_real_ts64(&ts);
time64_to_tm(ts.tv_sec, 0, &tm_time);
printk(KERN_INFO "UTC Time: %04d-%02d-%02d %02d:%02d:%02d\n", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec);
return 0; }
static void __exit example_exit(void) { printk(KERN_INFO "Module unloaded\n"); }
module_init(example_init); module_exit(example_exit);
|
4. 编译与加载
Makefile 示例:
1 2 3 4 5 6 7
| obj-m += example.o
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
操作步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| make
sudo insmod example.ko
dmesg | tail -n 2
sudo rmmod example
|
5. 注意事项
时区处理:
- 若需本地时间,需手动计算时区偏移(例如东八区为
8*3600 秒):1
| time64_to_tm(ts.tv_sec, 8 * 3600, &tm_time);
|
- 内核中不直接支持时区数据库,需硬编码偏移量。
时间精度:
time64_to_tm 仅处理秒级时间戳,纳秒部分(ts.tv_nsec)需额外处理。
年份和月份的修正:
tm_year 返回的是自 1900 年起的差值,需 +1900 得到实际年份。
tm_mon 范围是 0-11(0 代表 1 月),需 +1 得到实际月份。
函数安全性:
time64_to_tm 是内核安全函数,可在中断上下文中调用。
6. 对比用户空间函数
| 函数/操作 |
用户空间 |
内核空间 |
| 时间戳转分解时间 |
localtime() |
time64_to_tm() |
| 时间戳来源 |
time() |
ktime_get_real_ts64() |
| 时区支持 |
自动读取系统时区 |
需手动指定偏移量 |
7. 扩展应用
将分解时间格式化为字符串
若需生成自定义时间字符串(如 YYYY-MM-DD HH:MM:SS),可结合 snprintf:
1 2 3 4 5 6 7 8 9
| char time_buf[64]; snprintf(time_buf, sizeof(time_buf), "%04d-%02d-%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); printk(KERN_INFO "Formatted Time: %s\n", time_buf);
|
通过上述示例,可以轻松在内核模块中使用 time64_to_tm 将时间戳转换为可读格式,适用于日志记录、调试等场景。
使用time64_to_tm接口获取本地时间的方法及示例
在内核中使用 time64_to_tm 获取本地时间需要 手动指定时区偏移量(因为内核不直接支持系统时区配置)。以下是具体方法及示例:
1. 核心思路
- 获取 UTC 时间戳:通过
ktime_get_real_ts64 获取当前 UTC 时间(64 位秒数)。
- 计算时区偏移:本地时间 = UTC 时间 + 时区偏移(例如东八区偏移为
8*3600 秒)。
- 转换时间格式:调用
time64_to_tm 将时间戳转换为分解时间(struct tm)。
2. 完整示例代码
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
| #include <linux/module.h> #include <linux/time.h> #include <linux/timekeeping.h>
MODULE_LICENSE("GPL"); MODULE_AUTHOR("Your Name"); MODULE_DESCRIPTION("Get local time with time64_to_tm");
static int __init example_init(void) { struct timespec64 ts_utc; struct tm tm_local; int timezone_offset = 8 * 3600;
ktime_get_real_ts64(&ts_utc);
time64_to_tm(ts_utc.tv_sec + timezone_offset, 0, &tm_local);
printk(KERN_INFO "Local Time: %04d-%02d-%02d %02d:%02d:%02d\n", tm_local.tm_year + 1900, tm_local.tm_mon + 1, tm_local.tm_mday, tm_local.tm_hour, tm_local.tm_min, tm_local.tm_sec);
return 0; }
static void __exit example_exit(void) { printk(KERN_INFO "Module unloaded\n"); }
module_init(example_init); module_exit(example_exit);
|
3. 编译与验证
Makefile 示例:
1 2 3 4 5 6 7
| obj-m += local_time.o
all: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean: make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
|
操作步骤:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| make
sudo insmod local_time.ko
dmesg | tail -n 1
sudo rmmod local_time
|
4. 关键细节说明
(1) 时区偏移计算
- 公式:
本地时间 = UTC时间 + 时区偏移
- 示例:
- 东八区(北京):
8小时 = 8*3600秒
- 西五区(纽约):
-5小时 = -5*3600秒
(2) 夏令时(DST)问题
- 内核不自动处理夏令时,若需支持,需手动调整偏移量(例如东八区夏令时偏移为
+9*3600)。
- 实际应用中,硬编码偏移量可能不灵活,需根据业务需求处理。
(3) 时区偏移来源
- 硬编码:简单但不可动态调整(如示例中的
timezone_offset = 8*3600)。
- 内核参数传递:通过模块参数动态设置偏移量:
1 2 3
| static int timezone_offset = 8*3600; module_param(timezone_offset, int, 0644); MODULE_PARM_DESC(timezone_offset, "Timezone offset in seconds (e.g., 28800 for UTC+8)");
|
加载模块时指定:1
| sudo insmod local_time.ko timezone_offset=28800
|
5. 对比用户空间行为
| 操作 |
用户空间 |
内核空间 |
| 获取时间 |
time() / gettimeofday() |
ktime_get_real_ts64() |
| 转换本地时间 |
localtime()(自动读时区) |
time64_to_tm() + 手动偏移 |
| 时区配置 |
/etc/localtime 或 TZ 变量 |
需硬编码或动态传递偏移量 |
6. 扩展应用:动态获取系统时区
若需在内核中动态获取系统时区偏移,需结合用户空间协作(需 root 权限):
1 2 3 4 5 6 7 8 9 10 11
|
static int get_system_timezone_offset(void) { return 8*3600; }
timezone_offset = get_system_timezone_offset();
|
7. 总结
- 核心步骤:获取 UTC 时间 → 添加时区偏移 → 调用
time64_to_tm。
- 适用场景:内核日志时间戳、驱动程序时间记录等。
- 局限性:时区偏移需手动维护,无法直接响应系统时区变化。
Linux下获取时区接口
在 Linux 系统中,获取时区信息可以通过多种接口实现,具体分为 用户空间 和 内核空间 两类场景。以下是详细的接口说明及示例:
一、用户空间获取时区
1. 使用 localtime 和 tzset 函数
通过标准库函数读取系统时区配置,依赖环境变量 TZ 或系统默认时区文件 /etc/localtime。
示例代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| #include <stdio.h> #include <time.h> #include <stdlib.h>
int main() { time_t t = time(NULL); struct tm *local = localtime(&t);
tzset();
printf("Timezone Name: %s\n", tzname[0]); printf("UTC Offset: %ld hours\n", timezone / 3600); printf("Local Time: %s", asctime(local));
return 0; }
|
输出示例:
1 2 3
| Timezone Name: CST UTC Offset: -8 hours Local Time: Wed Oct 6 15:30:45 2023
|
关键点:
tzname[0]: 时区名称(非夏令时)。
timezone: 时区偏移(秒),UTC 时间 = 本地时间 + timezone(东区为负值,西区为正值)。
daylight: 是否启用夏令时(0 或 1)。
2. 直接读取 /etc/localtime 文件
系统时区通常通过符号链接 /etc/localtime 指向 /usr/share/zoneinfo/ 中的时区文件。
示例命令:
1 2 3 4 5
| ls -l /etc/localtime
lrwxrwxrwx 1 root root 33 Oct 1 12:00 /etc/localtime -> /usr/share/zoneinfo/Asia/Shanghai
|
解析时区名称:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> #include <unistd.h> #include <limits.h>
int main() { char tzpath[PATH_MAX]; ssize_t len = readlink("/etc/localtime", tzpath, sizeof(tzpath)-1); if (len != -1) { tzpath[len] = '\0'; printf("Timezone File: %s\n", tzpath); } return 0; }
|
3. 使用 gettimeofday(已废弃时区参数)
尽管 gettimeofday 的 struct timezone 参数已过时,仍可通过它获取粗略的时区偏移:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <sys/time.h> #include <stdio.h>
int main() { struct timeval tv; struct timezone tz;
gettimeofday(&tv, &tz); printf("Timezone Offset: UTC%d%02d\n", -tz.tz_minuteswest / 60, tz.tz_minuteswest % 60); return 0; }
|
输出示例:
1
| Timezone Offset: UTC+08:00
|
二、内核空间获取时区
Linux 内核默认 不直接管理时区,系统时间以 UTC 存储。若需在内核模块中获取时区信息,需通过以下方法:
1. 硬编码时区偏移
在模块中手动指定时区偏移(如东八区):
1 2 3 4 5 6 7 8
| #include <linux/time.h>
static int timezone_offset = 8 * 3600;
struct timespec64 ts_utc; ktime_get_real_ts64(&ts_utc); time64_t local_sec = ts_utc.tv_sec + timezone_offset;
|
2. 通过用户空间传递时区
通过内核与用户空间通信(如 sysfs、netlink 或模块参数)动态获取时区偏移。
示例:模块参数传递时区
1 2 3 4 5 6 7 8
| #include <linux/module.h>
static int timezone_offset = 0; module_param(timezone_offset, int, 0644); MODULE_PARM_DESC(timezone_offset, "Timezone offset in seconds (e.g., 28800 for UTC+8)");
|
3. 读取 RTC 时间(可能包含时区)
部分系统将 RTC 时间设置为本地时间(而非 UTC),可通过 RTC 驱动接口获取:
1 2 3 4 5 6 7 8 9 10
| #include <linux/rtc.h>
struct rtc_time tm; struct rtc_device *rtc = rtc_class_open("rtc0"); rtc_read_time(rtc, &tm); rtc_class_close(rtc);
printk("RTC Time: %04d-%02d-%02d %02d:%02d:%02d\n", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec);
|
三、动态获取系统时区(高级)
1. 解析 /proc/timedate 信息
通过 /proc/timedate 文件读取时区名称(需内核支持):
1 2 3
| cat /proc/timedate
Timezone: Asia/Shanghai (CST, +0800)
|
2. 使用 Glibc 扩展函数 gettimeofday
通过 struct timezone 获取时区偏移(注意:非跨平台兼容):
1 2 3 4 5 6 7
| #include <sys/time.h>
void print_timezone() { struct timezone tz; gettimeofday(NULL, &tz); printf("Timezone Offset: %d minutes west of UTC\n", tz.tz_minuteswest); }
|
四、总结
| 场景 |
方法 |
特点 |
| 用户空间 |
localtime + tzset |
依赖系统时区配置,支持动态调整 |
|
读取 /etc/localtime 符号链接 |
直接获取时区文件路径 |
|
gettimeofday(时区参数) |
已废弃,但可获取粗略偏移 |
| 内核空间 |
硬编码时区偏移 |
简单但不灵活 |
|
通过模块参数传递 |
动态配置,依赖用户输入 |
|
读取 RTC 时间(若设置为本地时间) |
依赖硬件配置 |
注意事项
时区与夏令时:
- 用户空间可通过
tzname[1] 获取夏令时时区名称(如 CDT),但内核无内置支持。
- 夏令时偏移需手动处理。
内核限制:
- 内核不直接管理时区,需依赖外部输入或硬编码。
- 修改系统时区需通过用户空间工具(如
timedatectl)。
跨平台兼容性:
- 避免依赖
/proc 或 /sys 中的非标准文件。
- 优先使用标准库函数(如
localtime_r)。
通过上述接口,开发者可以在不同场景下灵活获取和处理时区信息。