【代码模板】-Linux内核获取时间的接口和KO例子和一些接口

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, &timestamp_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文件系统提供简单的用户接口,方便测试和查看当前时间信息。代码兼容不同内核版本,可作为开发参考。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值