gettimeofday与settimeofday用法详解
gettimeofday和settimeofday的用法及示例
gettimeofday
和 settimeofday
是用于获取和设置系统时间的系统调用,适用于需要高精度时间操作的场景。以下是它们的详细用法及示例:
1. gettimeofday
函数原型:
1 |
|
参数说明:
- tv:指向
struct timeval
的指针,用于存储获取的时间(精确到微秒)。 - tz:时区信息,通常已废弃,设置为
NULL
。
struct timeval
结构:
1 | struct timeval { |
示例:获取当前时间
1 |
|
2. settimeofday
函数原型:
1 |
|
参数说明:
- tv:指向
struct timeval
的指针,包含要设置的时间。 - tz:时区信息,通常设置为
NULL
。
注意事项:
- 需要 root 权限(如使用
sudo
运行程序)。 - 直接修改系统时间可能导致不可预见的后果(如日志混乱),需谨慎使用。
示例:设置系统时间
1 |
|
注意事项
时区处理:
mktime
默认将struct tm
视为本地时间。若需使用 UTC 时间,可使用timegm
(非标准,需在 Linux 中定义_BSD_SOURCE
)或调整时区:处理完毕后恢复原时区。1
2setenv("TZ", "UTC", 1); // 设置时区为 UTC
tzset(); // 生效时区设置
权限问题:
- 运行
settimeofday
示例时需使用sudo
:1
sudo ./settime_example
- 运行
替代方案:
- 对于时间调整,推荐使用
ntp
服务或adjtime
逐步校正。 - 新代码建议使用
clock_gettime
和clock_settime
(精度更高)。
- 对于时间调整,推荐使用
通过上述示例和说明,可安全地使用 gettimeofday
获取时间,并在必要时用 settimeofday
设置系统时间。
Linux内核ktime_get_real_ts64函数用法及示例
ktime_get_real_ts64
是 Linux 内核中用于获取当前真实时间(即“墙上时间”,wall time)的高精度函数,返回的时间格式为 timespec64
(64 位秒和纳秒)。以下是详细用法及示例:
1. 函数原型与头文件
1 |
|
参数说明:
- ts:指向
struct timespec64
的指针,用于存储获取的时间。
struct timespec64
结构:
1 | struct timespec64 { |
2. 使用场景
- 在内核模块中获取高精度实时时间(如日志记录、性能分析)。
- 需要避免用户空间与内核空间上下文切换的开销时。
- 支持 64 位时间(避免 2038 年溢出问题)。
3. 示例代码
内核模块示例:获取并打印当前时间
1 |
|
4. 编译与加载
Makefile 示例:
1 | obj-m += example.o |
操作步骤:
1 | # 编译模块 |
5. 注意事项
内核上下文安全性:
ktime_get_real_ts64
可以在大多数上下文中调用(包括中断上下文),但需确认具体内核版本的实现。- 避免在持有锁时调用,以减少延迟。
时间精度:
- 返回的时间精度为纳秒级,但实际精度取决于硬件和内核配置(如
CONFIG_HIGH_RES_TIMERS
)。
- 返回的时间精度为纳秒级,但实际精度取决于硬件和内核配置(如
时间来源:
- 返回的时间是系统实时时间,可能受 NTP 调整或
settimeofday
影响。
- 返回的时间是系统实时时间,可能受 NTP 调整或
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 |
|
通过上述示例和说明,可以在内核模块中安全地使用 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 |
|
参数说明:
- **
totalsecs
**:待转换的 64 位时间戳(秒数)。 - **
offset
**:时区偏移(单位:秒)。若设为0
,表示使用 UTC 时间;若需本地时间,需传入时区偏移(如东八区为8*3600
)。 - **
result
**:指向struct tm
的指针,用于存储转换后的分解时间。
struct tm
结构:
1 | struct tm { |
2. 使用场景
- 在内核模块中将时间戳转换为可读的年、月、日、时、分、秒。
- 生成日志时间戳、调试信息或处理时间相关逻辑。
3. 示例代码
内核模块示例:将时间戳转换为分解时间并打印
1 |
|
4. 编译与加载
Makefile 示例:
1 | obj-m += example.o |
操作步骤:
1 | # 编译模块 |
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 | char time_buf[64]; |
通过上述示例,可以轻松在内核模块中使用 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 |
|
3. 编译与验证
Makefile 示例:
1 | obj-m += local_time.o |
操作步骤:
1 | # 编译模块 |
4. 关键细节说明
(1) 时区偏移计算
- 公式:
本地时间 = UTC时间 + 时区偏移
- 示例:
- 东八区(北京):
8小时 = 8*3600秒
- 西五区(纽约):
-5小时 = -5*3600秒
- 东八区(北京):
(2) 夏令时(DST)问题
- 内核不自动处理夏令时,若需支持,需手动调整偏移量(例如东八区夏令时偏移为
+9*3600
)。 - 实际应用中,硬编码偏移量可能不灵活,需根据业务需求处理。
(3) 时区偏移来源
- 硬编码:简单但不可动态调整(如示例中的
timezone_offset = 8*3600
)。 - 内核参数传递:通过模块参数动态设置偏移量:加载模块时指定:
1
2
3static 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 | // 示例:通过系统调用或 procfs 获取用户空间时区偏移 |
7. 总结
- 核心步骤:获取 UTC 时间 → 添加时区偏移 → 调用
time64_to_tm
。 - 适用场景:内核日志时间戳、驱动程序时间记录等。
- 局限性:时区偏移需手动维护,无法直接响应系统时区变化。
Linux下获取时区接口
在 Linux 系统中,获取时区信息可以通过多种接口实现,具体分为 用户空间 和 内核空间 两类场景。以下是详细的接口说明及示例:
一、用户空间获取时区
1. 使用 localtime
和 tzset
函数
通过标准库函数读取系统时区配置,依赖环境变量 TZ
或系统默认时区文件 /etc/localtime
。
示例代码:
1 |
|
输出示例:
1 | Timezone Name: CST |
关键点:
tzname[0]
: 时区名称(非夏令时)。timezone
: 时区偏移(秒),UTC 时间 = 本地时间 + timezone(东区为负值,西区为正值)。daylight
: 是否启用夏令时(0 或 1)。
2. 直接读取 /etc/localtime
文件
系统时区通常通过符号链接 /etc/localtime
指向 /usr/share/zoneinfo/
中的时区文件。
示例命令:
1 | # 查看当前时区文件路径 |
解析时区名称:
1 |
|
3. 使用 gettimeofday
(已废弃时区参数)
尽管 gettimeofday
的 struct timezone
参数已过时,仍可通过它获取粗略的时区偏移:
1 |
|
输出示例:
1 | Timezone Offset: UTC+08:00 |
二、内核空间获取时区
Linux 内核默认 不直接管理时区,系统时间以 UTC 存储。若需在内核模块中获取时区信息,需通过以下方法:
1. 硬编码时区偏移
在模块中手动指定时区偏移(如东八区):
1 |
|
2. 通过用户空间传递时区
通过内核与用户空间通信(如 sysfs、netlink 或模块参数)动态获取时区偏移。
示例:模块参数传递时区
1 |
|
3. 读取 RTC 时间(可能包含时区)
部分系统将 RTC 时间设置为本地时间(而非 UTC),可通过 RTC 驱动接口获取:
1 |
|
三、动态获取系统时区(高级)
1. 解析 /proc/timedate
信息
通过 /proc/timedate
文件读取时区名称(需内核支持):
1 | cat /proc/timedate |
2. 使用 Glibc 扩展函数 gettimeofday
通过 struct timezone
获取时区偏移(注意:非跨平台兼容):
1 |
|
四、总结
场景 | 方法 | 特点 |
---|---|---|
用户空间 | localtime + tzset |
依赖系统时区配置,支持动态调整 |
读取 /etc/localtime 符号链接 |
直接获取时区文件路径 | |
gettimeofday (时区参数) |
已废弃,但可获取粗略偏移 | |
内核空间 | 硬编码时区偏移 | 简单但不灵活 |
通过模块参数传递 | 动态配置,依赖用户输入 | |
读取 RTC 时间(若设置为本地时间) | 依赖硬件配置 |
注意事项
时区与夏令时:
- 用户空间可通过
tzname[1]
获取夏令时时区名称(如CDT
),但内核无内置支持。 - 夏令时偏移需手动处理。
- 用户空间可通过
内核限制:
- 内核不直接管理时区,需依赖外部输入或硬编码。
- 修改系统时区需通过用户空间工具(如
timedatectl
)。
跨平台兼容性:
- 避免依赖
/proc
或/sys
中的非标准文件。 - 优先使用标准库函数(如
localtime_r
)。
- 避免依赖
通过上述接口,开发者可以在不同场景下灵活获取和处理时区信息。