1. 背景
本文是Linux内核常用时间戳的例子ko。方便平时快速取用
2. 例子
2.1 KO 源码
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/jiffies.h>
#include <linux/time.h>
#include <linux/timer.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include <linux/version.h>
MODULE_LICENSE("GPL");
MODULE_AUTHOR("beiming");
MODULE_DESCRIPTION("A module to demonstrate Linux kernel timestamp APIs");
MODULE_VERSION("0.1");
// Function to demonstrate jiffies usage
static void demonstrate_jiffies(void)
{
unsigned long jiffy_val;
unsigned int jiffy_rate;
unsigned long jiffies_to_secs;
unsigned long msecs_val;
printk(KERN_INFO "=== Jiffies Demonstration ===\n");
// Get current jiffies value
jiffy_val = jiffies;
printk(KERN_INFO "Current jiffies value: %lu\n", jiffy_val);
// Get jiffies rate (HZ)
jiffy_rate = HZ;
printk(KERN_INFO "Jiffies rate (HZ): %u\n", jiffy_rate);
// Convert jiffies to milliseconds
msecs_val = jiffies_to_msecs(jiffy_val);
printk(KERN_INFO "Jiffies converted to milliseconds: %lu\n", msecs_val);
// Convert milliseconds to jiffies
msecs_val = 5000; // 5 seconds in milliseconds
jiffies_to_secs = msecs_to_jiffies(msecs_val);
printk(KERN_INFO "5000 milliseconds in jiffies: %lu\n", jiffies_to_secs);
// Time comparison with jiffies
if (time_after(jiffy_val, jiffy_val - 100))
printk(KERN_INFO "time_after: jiffies is after (jiffies - 100)\n");
if (time_before(jiffy_val, jiffy_val + 100))
printk(KERN_INFO "time_before: jiffies is before (jiffies + 100)\n");
}
// Function to demonstrate kernel time APIs
static void demonstrate_kernel_time(void)
{
struct timespec64 ts;
s64 timestamp_ns;
printk(KERN_INFO "=== Kernel Time APIs Demonstration ===\n");
// Get current monotonic time
ktime_get_ts64(&ts);
printk(KERN_INFO "Monotonic time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);
// Get current boot time
ktime_get_boottime_ts64(&ts);
printk(KERN_INFO "Boot time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);
// Get timestamp in nanoseconds
timestamp_ns = ktime_get_ns();
printk(KERN_INFO "Timestamp (ns): %lld\n", timestamp_ns);
// Get real time
ktime_get_real_ts64(&ts);
printk(KERN_INFO "Real time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);
}
// Function to demonstrate timer usage
static struct timer_list demo_timer;
static void timer_callback(struct timer_list *t)
{
printk(KERN_INFO "Timer callback executed at jiffies: %lu\n", jiffies);
// Reschedule the timer for 2 seconds later
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(2000));
}
static void demonstrate_timers(void)
{
printk(KERN_INFO "=== Timer Demonstration ===\n");
// Initialize timer (for kernel 4.18, we need to use init_timer)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
timer_setup(&demo_timer, timer_callback, 0);
#else
init_timer(&demo_timer);
demo_timer.function = (void (*)(unsigned long))timer_callback;
demo_timer.data = 0;
#endif
// Set timer to expire in 1 second
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(1000));
printk(KERN_INFO "Timer set to expire in 1 second\n");
}
// Function to demonstrate time conversion utilities
static void demonstrate_time_conversions(void)
{
unsigned long msec, usec;
unsigned long jiff;
printk(KERN_INFO "=== Time Conversion Utilities ===\n");
// Convert various time units to jiffies
msec = 1000; // 1 second in milliseconds
usec = 1000000; // 1 second in microseconds
jiff = msecs_to_jiffies(msec);
printk(KERN_INFO "%lu milliseconds = %lu jiffies\n", msec, jiff);
jiff = usecs_to_jiffies(usec);
printk(KERN_INFO "%lu microseconds = %lu jiffies\n", usec, jiff);
jiff = msecs_to_jiffies(5000); // 5 seconds worth of jiffies
msec = jiffies_to_msecs(jiff);
usec = jiffies_to_usecs(jiff);
printk(KERN_INFO "%lu jiffies = %lu milliseconds\n", jiff, msec);
printk(KERN_INFO "%lu jiffies = %lu microseconds\n", jiff, usec);
}
// Proc interface for testing
static struct proc_dir_entry *proc_entry;
static int timestamp_proc_show(struct seq_file *m, void *v)
{
seq_printf(m, "Linux Kernel Timestamp Examples:\n");
seq_printf(m, "================================\n\n");
seq_printf(m, "1. Jiffies value: %lu\n", jiffies);
seq_printf(m, "2. Jiffies rate (HZ): %u\n", HZ);
seq_printf(m, "3. Jiffies to milliseconds: %u\n", jiffies_to_msecs(jiffies));
return 0;
}
static int timestamp_proc_open(struct inode *inode, struct file *file)
{
return single_open(file, timestamp_proc_show, NULL);
}
// For older kernels, we need to use file_operations instead of proc_ops
static const struct file_operations timestamp_proc_fops = {
.owner = THIS_MODULE,
.open = timestamp_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
// Module initialization
static int __init timestamp_module_init(void)
{
printk(KERN_INFO "Timestamp Module Loaded\n");
// Demonstrate various timestamp APIs
demonstrate_jiffies();
demonstrate_kernel_time();
demonstrate_time_conversions();
demonstrate_timers();
// Create proc entry for testing
proc_entry = proc_create("timestamp_demo", 0666, NULL, ×tamp_proc_fops);
if (!proc_entry) {
printk(KERN_ERR "Failed to create proc entry\n");
return -ENOMEM;
}
printk(KERN_INFO "Timestamp demo proc entry created at /proc/timestamp_demo\n");
return 0;
}
// Module exit
static void __exit timestamp_module_exit(void)
{
// Clean up timer
del_timer(&demo_timer);
// Remove proc entry
proc_remove(proc_entry);
printk(KERN_INFO "Timestamp Module Unloaded\n");
}
module_init(timestamp_module_init);
module_exit(timestamp_module_exit);
2.2 makefile:
obj-m += timestamp_module.o
KDIR := /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)
all:
make -C $(KDIR) M=$(PWD) modules
clean:
make -C $(KDIR) M=$(PWD) clean
install:
sudo insmod timestamp_module.ko
uninstall:
sudo rmmod timestamp_module
reload: uninstall install
test:
cat /proc/timestamp_demo
view-logs:
sudo dmesg | tail -20
编译和加载
make
sudo insmod timestamp_module.ko #或者sudo make install
加载日志:
[170171.123492] Timestamp Module Loaded
[170171.123495] === Jiffies Demonstration ===
[170171.123496] Current jiffies value: 4464832745
[170171.123497] Jiffies rate (HZ): 1000
[170171.123498] Jiffies converted to milliseconds: 169865449
[170171.123499] 5000 milliseconds in jiffies: 5000
[170171.123500] time_after: jiffies is after (jiffies - 100)
[170171.123500] time_before: jiffies is before (jiffies + 100)
[170171.123501] === Kernel Time APIs Demonstration ===
[170171.123501] Monotonic time: 170165.448526825 seconds
[170171.123503] Boot time: 170165.448528261 seconds
[170171.123504] Timestamp (ns): 170165448529418
[170171.123505] Real time: 1756857047.239262119 seconds
[170171.123506] === Time Conversion Utilities ===
[170171.123507] 1000 milliseconds = 1000 jiffies
[170171.123508] 1000000 microseconds = 1000 jiffies
[170171.123509] 5000 jiffies = 5000 milliseconds
[170171.123509] 5000 jiffies = 5000000 microseconds
[170171.123510] === Timer Demonstration ===
[170171.123512] Timer set to expire in 1 second
[170171.123517] Timestamp demo proc entry created at /proc/timestamp_demo
[170172.171035] Timer callback executed at jiffies: 4464833793
[170174.218095] Timer callback executed at jiffies: 4464835840
[170176.266161] Timer callback executed at jiffies: 4464837888
[170178.314226] Timer callback executed at jiffies: 4464839936
[170180.362289] Timer callback executed at jiffies: 4464841984
[170182.410354] Timer callback executed at jiffies: 4464844032
[170184.253930] Timestamp Module Unloaded
[170219.428102] Timestamp Module Loaded
[170219.428105] === Jiffies Demonstration ===
[170219.428106] Current jiffies value: 4464881048
[170219.428107] Jiffies rate (HZ): 1000
[170219.428107] Jiffies converted to milliseconds: 169913752
[170219.428108] 5000 milliseconds in jiffies: 5000
[170219.428109] time_after: jiffies is after (jiffies - 100)
[170219.428109] time_before: jiffies is before (jiffies + 100)
[170219.428109] === Kernel Time APIs Demonstration ===
[170219.428110] Monotonic time: 170213.751619858 seconds
[170219.428111] Boot time: 170213.751621096 seconds
[170219.428112] Timestamp (ns): 170213751621799
[170219.428113] Real time: 1756857095.542354165 seconds
[170219.428113] === Time Conversion Utilities ===
[170219.428114] 1000 milliseconds = 1000 jiffies
[170219.428114] 1000000 microseconds = 1000 jiffies
[170219.428115] 5000 jiffies = 5000 milliseconds
[170219.428115] 5000 jiffies = 5000000 microseconds
[170219.428116] === Timer Demonstration ===
[170219.428117] Timer set to expire in 1 second
[170219.428121] Timestamp demo proc entry created at /proc/timestamp_demo
[170219.457061] Timestamp Module Unloaded
关键点说明:
关于jiffies以及如何获取?
- 1 个 “jiffy”,即一次系统时钟中断(tick)
- 和HZ什么关系:HZ 定义了每秒产生多少次 tick,所以 1 jiffy = 1/HZ 秒
- 每秒增加多少个 jiffies :HZ 个(HZ 就是每秒 tick 次数)
- 初始值:内核在启动时把 jiffies_64 设为接近 32 位回绕的一个值(典型是 (-300*HZ)),以便在开发阶段尽快暴露 32 位 jiffies 回绕 bug
- 精度:在 HZ=1000 且没有启用高精度事件定时器(HPET / NO_HZ_FULL / tickless idle)的“传统 tick”内核里,每 1 ms 就会触发一次 时钟中断(tick),从而把 jiffies/jiffies_64 加 1。
- 高精度:CONFIG_HIGH_RES_TIMERS。现代内核普遍启用 CONFIG_HIGH_RES_TIMERS,它用 HPET/ TSC-deadline 等硬件,把 1 ms 的粗粒度 tick 拆成 纳秒级 的调度,但对 jiffies 的取值逻辑不变——只在每次真正发生 tick 时才累加,因此即便高精度定时器生效,jiffies 仍然是 1 ms 精度(只是 tick 可能偶尔缺勤)。
- 注意点1:tickless 内核(CONFIG_NO_HZ_*),如果内核开启了 CONFIG_NO_HZ_IDLE(或 CONFIG_NO_HZ_FULL),当 CPU 进入 idle 或只有一个运行任务时,tick 会被动态关闭,中断间隔不再是固定 1 ms,而是由下一次需要调度/定时的事件决定,可以几毫秒甚至几十毫秒才来一次中断。此时 jiffies 的更新也不再严格按 1 ms 步进,而是根据实际流逝时间一次性“跳”若干单位。
获取可以通过直接读取变量,或者接口
// Get current jiffies value
jiffy_val = jiffies;
printk(KERN_INFO "Current jiffies value: %lu\n", jiffy_val);

HZ如何获取?
// Get jiffies rate (HZ)
jiffy_rate = HZ;
printk(KERN_INFO "Jiffies rate (HZ): %u\n", jiffy_rate);

Jiffies如何转化为ms
// Convert jiffies to milliseconds
msecs_val = jiffies_to_msecs(jiffy_val);
printk(KERN_INFO "Jiffies converted to milliseconds: %lu\n", msecs_val);

如何根据jiffies来判断两个时间戳先后关系
// Time comparison with jiffies
if (time_after(jiffy_val, jiffy_val - 100))
printk(KERN_INFO "time_after: jiffies is after (jiffies - 100)\n");
if (time_before(jiffy_val, jiffy_val + 100))
printk(KERN_INFO "time_before: jiffies is before (jiffies + 100)\n");

如何设置timer的定时器以及定期回调
//定义
// Function to demonstrate timer usage
static struct timer_list demo_timer;
//初始化
// Initialize timer (for kernel 4.18, we need to use init_timer)
#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 15, 0)
timer_setup(&demo_timer, timer_callback, 0);
#else
init_timer(&demo_timer);
//指定回调函数和data的偏移
demo_timer.function = (void (*)(unsigned long))timer_callback;
demo_timer.data = 0;
#endif
//启动下次触发时间
// Set timer to expire in 1 second
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(1000));
printk(KERN_INFO "Timer set to expire in 1 second\n");
//回调函数,需要包含需要处理的事情,以及触发下次2庙后进行调度
static void timer_callback(struct timer_list *t)
{
printk(KERN_INFO "Timer callback executed at jiffies: %lu\n", jiffies);
// Reschedule the timer for 2 seconds later
mod_timer(&demo_timer, jiffies + msecs_to_jiffies(2000));
}

如何获取ktime格式的值(ts的sec和nsec)
struct timespec64 ts;
// Get current monotonic time
ktime_get_ts64(&ts);
printk(KERN_INFO "Monotonic time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);
获取出来的就是时间戳秒和毫秒。这表示的是从系统启动到现在的秒。
和jiffies的关系是jiffies/HZ的值。
什么是Monotonic time?
Monotonic time(单调时间,或译“单调时钟”)指只增不减、永远向前、不受系统时间被人为修改或 NTP 跳变影响的一种时间基准。
- 起点任意
不一定是 1970-01-01,而是系统启动或其他固定参考点;绝对值无意义,差值有意义。 - 永不回退
即使管理员执行 date -s 或 NTP 回调,monotonic 时钟仍持续递增。 - 用户态获取
Linux 上常用 CLOCK_MONOTONIC(纳秒级),clock_gettime() 可获取;
要点:Monotonic time = “只会向前走的秒表”,专为测量间隔而生,不怕系统时间被改。
如何获取boottime
// Get current boot time
ktime_get_boottime_ts64(&ts);
printk(KERN_INFO "Boot time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);
从系统启动开始过了多少s和ns,不是墙上时间
如何获取纳秒
从系统启动后过了多少ns
s64 timestamp_ns;
// Get timestamp in nanoseconds
timestamp_ns = ktime_get_ns();
printk(KERN_INFO "Timestamp (ns): %lld\n", timestamp_ns);
如何获取真实时间(墙上时间)
// Get real time
ktime_get_real_ts64(&ts);
printk(KERN_INFO "Real time: %lld.%09ld seconds\n",
(s64)ts.tv_sec, ts.tv_nsec);


综述
本文提供了一个Linux内核模块示例,用于演示常见的时间戳API使用方式。主要内容包括:1) jiffies相关操作,包括获取当前值、转换毫秒/微秒与jiffies之间的转换、时间比较等;2) 内核时间API,如获取单调时间、启动时间、纳秒级时间戳和实时时间;3) 定时器功能演示,包括初始化和回调处理;4) 时间转换工具函数。该模块还通过proc文件系统提供简单的用户接口,方便测试和查看当前时间信息。代码兼容不同内核版本,可作为开发参考。

876

被折叠的 条评论
为什么被折叠?



