Linux时间子系统1

Linux时间子系统1(基于Linux6.6)---timer_list介绍

 


一、概述

在Linux内核中,timer_list 是定时器机制相关的重要数据结构。在内核中用于处理时间相关的任务,如延时执行、周期性任务以及超时处理等。定时器在内核中广泛应用于调度、驱动程序、网络协议栈、文件系统等方面。

内核节点来表示TimerList统计信息。

/proc/timer_list,打印per_cpu的hrtimer_bases信息以及基于此的timer列表,包括三种时钟MONOTONIC/REALTIME/BOOTTIME;以及Broadcast Tick Device和Per CPU Tick Device信息。

 

二、Timer_list

2.1、节点创建

kernel/time/timer_list.c中init_timer_list_procfs创建/proc/timer_list节点。

static int __init init_timer_list_procfs(void)
{
	struct proc_dir_entry *pe;

	pe = proc_create_seq_private("timer_list", 0400, NULL, &timer_list_sops,
			sizeof(struct timer_list_iter), NULL);
	if (!pe)
		return -ENOMEM;
	return 0;
}
__initcall(init_timer_list_procfs);
static int timer_list_show(struct seq_file *m, void *v)
{
	struct timer_list_iter *iter = v;

	if (iter->cpu == -1 && !iter->second_pass)
		timer_list_header(m, iter->now);
	else if (!iter->second_pass)
		print_cpu(m, iter->cpu, iter->now);
#ifdef CONFIG_GENERIC_CLOCKEVENTS
	else if (iter->cpu == -1 && iter->second_pass)
		timer_list_show_tickdevices_header(m);
	else
		print_tickdevice(m, tick_get_device(iter->cpu), iter->cpu);
#endif
	return 0;
}

 

下面根据一个示例来分析:

Timer List Version: v0.8
HRTIMER_MAX_CLOCK_BASES: 8
now at 348005493993702 nsecs

cpu: 0-----------------------print_cpuCPU0的信息如下,从clock0到clock2
clock 0:---------------------clock0作为MONOTONIC使用ktime_get获取当前时间,是timerkeeper的xtime和wall_to_monotonic之和。
.base: ffff9270aa21c000
.index: 0
.resolution: 1 nsecs
.get_time: ktime_get
.offset: 0 nsecs------------和MONOTONIC相比的offset。
active timers:--------------print_active_timers打印所有的hrtimer,按照时间排列,timerqueue_iterate_next读取rbtree的next。

--依次是:序列号、hrtimer、timer状态、hrtimer超时函数、进程名/进程号

--超时绝对时间和相对时间。
active timers:
 #0: <ffffbb12013cfeb0>, hrtimer_wakeup, S:01
 # expires at 348005494451191-348005494501191 nsecs [in 457489 to 507489 nsecs]
 #1: <ffff9270aa21c520>, tick_sched_timer, S:01
 # expires at 348005494627251-348005494627251 nsecs [in 633549 to 633549 nsecs]
 #2: <ffffbb1203c67a90>, hrtimer_wakeup, S:01
 # expires at 348005496929532-348005496979532 nsecs [in 2935830 to 2985830 nsecs]
 #3: <ffffbb120085fde8>, hrtimer_wakeup, S:01
 # expires at 348005497678070-348005497728070 nsecs [in 3684368 to 3734368 nsecs]
 #4: <ffffbb120164fde8>, hrtimer_wakeup, S:01
 # expires at 348005497683499-348005497733499 nsecs [in 3689797 to 3739797 nsecs]
 #5: <ffffbb1202e5fde8>, hrtimer_wakeup, S:01
 # expires at 348005498946047-348005498996047 nsecs [in 4952345 to 5002345 nsecs]
 #6: <ffffbb12013f7de8>, hrtimer_wakeup, S:01
 # expires at 348005498954370-348005499004370 nsecs [in 4960668 to 5010668 nsecs]                                                                                                             
 #7: <ffffbb1203aa7d08>, hrtimer_wakeup, S:01                                                                                                                                                 
 # expires at 348005501518861-348005501568861 nsecs [in 7525159 to 7575159 nsecs]                                                                                                             
 #8: <ffffbb12035a7d08>, hrtimer_wakeup, S:01                                                                                                                                                 
 # expires at 348005515269901-348005515319901 nsecs [in 21276199 to 21326199 nsecs]                                                                                                           
 #9: <ffffbb12039a7a90>, hrtimer_wakeup, S:01                                                                                                                                                 
 # expires at 348005573723599-348005573823598 nsecs [in 79729897 to 79829896 nsecs]                                                                                                           
 #10: <ffffbb1201607eb0>, hrtimer_wakeup, S:01                                                                                                                                                
 # expires at 348005727135370-348005727185370 nsecs [in 233141668 to 233191668 nsecs]                                                                                                         
 clock 1:
  .base:       ffff9270aa21c040
  .index:      1
  .resolution: 1 nsecs
  .get_time:   ktime_get_real
  .offset:     1733968404430557217 nsecs
active timers:
 #0: <ffffbb1201767d08>, hrtimer_wakeup, S:01
 # expires at 1734316409963709613-1734316409963759613 nsecs [in 39158694 to 39208694 nsecs]
 #1: <ffffbb1208e97d08>, hrtimer_wakeup, S:01
 # expires at 1734316410350643000-1734316410350693000 nsecs [in 426092081 to 426142081 nsecs]
 #2: <ffffbb1200b4bd08>, hrtimer_wakeup, S:01
 # expires at 1734316410505412000-1734316410505462000 nsecs [in 580861081 to 580911081 nsecs]
 #3: <ffffbb1200d37d08>, hrtimer_wakeup, S:01
 # expires at 1734316790547733147-1734316790547783147 nsecs [in 380623182228 to 380623232228 nsecs]
 #4: <ffffbb120080fd08>, hrtimer_wakeup, S:01
 # expires at 1734317622914085000-1734317622914135000 nsecs [in 1212989534081 to 1212989584081 nsecs]
 #5: <ffffbb1200d7fd08>, hrtimer_wakeup, S:01
 # expires at 1734401090496983400-1734401090497033400 nsecs [in 84680572432481 to 84680572482481 nsecs]
 clock 2:
  .base:       ffff9270aa21c080
  .index:      2
  .resolution: 1 nsecs
  .get_time:   ktime_get_boottime
  .offset:     0 nsecs
active timers:

-------------------------------------------------------------------hrtimer_cpu_base部分,参照结构体解释----------------------------------------------------------------------
.expires_next : 86212955000000 nsecs---------CPU层次看hrtimer信息,可以在上面找到对应的最近一次expires
.hres_active : 1
.nr_events : 4648009
.nr_retries : 4467
.nr_hangs : 0
.max_hang_time : 0 nsecs

---------------------------------------------------------------------tick_sched部分,参照结构体解释-----------------------------------------------------------------------------
.nohz_mode : 2--------------------------------------nohz_mode对应NOHZ_MODE_INACTIVE(0)、NOHZ_MODE_LOWRES(1)、NOHZ_MODE_HIGHRES(2)。
.idle_tick : 86212945000000 nsecs
.tick_stopped : 0
.idle_jiffies : 17182588
.idle_calls : 4632242
.idle_sleeps : 4573348
.idle_entrytime : 86212940790186 nsecs
.idle_waketime : 86212940790186 nsecs
.idle_exittime : 86212940790186 nsecs
.idle_sleeptime : 84593571589272 nsecs
.iowait_sleeptime: 0 nsecs
.last_jiffies : 17182588
.next_jiffies : 17182604
.idle_expires : 86213020000000 nsecs
jiffies: 17182590


Tick Device: mode: 1-----------------------Broadcast TickDevice
Broadcast device
Clock Event Device: <NULL>
tick_broadcast_mask: 00000000
tick_broadcast_oneshot_mask: 00000000


Tick Device: mode: 1---------------------per_cpu TickDevice,详细信息参照clock_event_device结构体解释。
Per CPU device: 0
Clock Event Device: xxx_ap_timer0
max_delta_ns: 660764185443
min_delta_ns: 15385
mult: 13958644
shift: 32
mode: 3
next_event: 86212955000000 nsecs
set_next_event: xxx_timer_set_next_event
set_mode: xxx_timer_set_mode
event_handler: hrtimer_interrupt
retries: 165992

2.2、timer_list 数据结构

timer_list 是内核中定时器的核心数据结构。它用于表示一个定时器以及与该定时器相关的回调函数。每个 timer_list 结构体都包含了定时器的基本信息,如到期时间、回调函数、定时器状态等。

timer_list 结构体定义

struct timer_list {
    struct hlist_node entry;         // 定时器链表节点,用于定时器队列管理
    unsigned long expires;           // 定时器到期的时间(相对于系统启动时间)
    void (*function)(struct timer_list *);  // 定时器到期时的回调函数
    unsigned long data;              // 用户数据,可以传递给回调函数
    int base;                        // 该定时器所属的定时器基础结构(定时器队列)
};

字段说明:

  • entry: 定时器结构体在定时器链表中的位置。定时器通过哈希链表(hlist_node)或红黑树(在高精度定时器中)链接起来。
  • expires: 定时器的到期时间,以系统启动时间为基准的时间戳(单位是“jiffies”)。jiffies 是内核时间的计时单位,它是一个全局计数器,表示自系统启动以来的“节拍”数。
  • function: 定时器到期时调用的回调函数。这个回调函数会在定时器到期时执行用户指定的操作。
  • data: 用户可以通过这个字段传递额外的数据给回调函数,通常是指向某个对象或结构体的指针。
  • base: 定时器所属的定时器基础结构(用于管理多个定时器的定时器队列)。

2.3、定时器的创建与操作

  • 初始化定时器: 内核通过 init_timer()timer_setup() 来初始化定时器,并指定定时器到期时执行的回调函数。

  • void timer_setup(struct timer_list *timer, void (*function)(struct timer_list *), unsigned long flags);
    
  • 启动定时器: 定时器通过 mod_timer() 函数启动,并设定定时器的到期时间。

  • int mod_timer(struct timer_list *timer, unsigned long expires);
    
  • 删除定时器: 定时器通过 del_timer() 删除,取消定时器的调用。

  • int del_timer(struct timer_list *timer);
    

2.4、定时器工作机制

内核定时器机制的核心是基于 jiffies 的定时器队列管理。定时器到期后,系统会通过内核的定时器中断处理程序调用定时器的回调函数。

三、举例应用

1. 基本的定时器使用例子:

我们将实现一个简单的例子,创建一个定时器,定时器到期时触发一个回调函数,该函数将在定时器到期后打印一条消息。

步骤1:定义一个定时器回调函数

这个回调函数将在定时器到期时被调用。我们会简单地打印一条消息,表示定时器触发。

#include <linux/timer.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/jiffies.h>

static struct timer_list my_timer;  // 定义一个定时器

// 定时器到期时的回调函数
void timer_callback(struct timer_list *t)
{
    pr_info("Timer expired at jiffies: %ld\n", jiffies);
}

步骤2:初始化和启动定时器

在模块的初始化函数中,我们将初始化定时器并设置定时器的到期时间。假设我们让定时器在 2 秒后到期(在 Linux 中,jiffies 是一个内核时间单位,通常 1 个 jiffy 是 1/100秒,具体取决于系统配置)。

static int __init my_module_init(void)
{
    pr_info("Module initialized\n");

    // 初始化定时器
    timer_setup(&my_timer, timer_callback, 0);  // 传入回调函数

    // 设置定时器到期时间:当前时间 + 2秒
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(2000));  // 2000ms = 2秒

    return 0;
}

步骤3:模块清理

在模块卸载时,我们需要删除定时器,以防止在模块卸载后定时器回调仍然被调用。

static void __exit my_module_exit(void)
{
    pr_info("Module exited\n");

    // 删除定时器,防止定时器回调函数在模块卸载后被调用
    del_timer_sync(&my_timer);
}

步骤4:模块定义

module_init(my_module_init);   // 模块初始化函数
module_exit(my_module_exit);   // 模块退出函数

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Linux Developer");
MODULE_DESCRIPTION("A simple timer_list example");

2. 详细解释

timer_setup 函数

void timer_setup(struct timer_list *timer, void (*function)(struct timer_list *), unsigned long flags);
  • timer: 指向 timer_list 结构的指针,这是我们要初始化的定时器。
  • function: 指定定时器到期时要调用的回调函数。
  • flags: 标志参数,通常设置为 0,表示普通定时器。

timer_setup 函数用于初始化 timer_list 结构,并为定时器设置回调函数。

mod_timer 函数

int mod_timer(struct timer_list *timer, unsigned long expires);
  • timer: 需要设置的定时器。
  • expires: 定时器到期时间,单位为 jiffies。我们通过 jiffies + msecs_to_jiffies(2000) 来设置定时器 2 秒后触发。

mod_timer 函数会根据当前的系统时间(jiffies)设置定时器的到期时间。如果定时器已经存在,它将重新设置定时器的到期时间。

del_timer_sync 函数

int del_timer_sync(struct timer_list *timer);
  • timer: 指向要删除的定时器。

del_timer_sync 函数会删除定时器,并等待定时器的回调函数执行完毕。如果回调函数正在执行,它会等待执行完成再删除定时器。这通常用于确保定时器在删除时不会被触发。

3. 模块加载与卸载

加载这个内核模块时,定时器将在 2 秒后触发,执行回调函数并打印消息。当卸载模块时,定时器会被删除。

# 编译并加载模块
make
sudo insmod timer_example.ko

# 查看内核日志,检查定时器回调输出
dmesg | tail

# 卸载模块
sudo rmmod timer_example

4. 扩展应用场景:周期性定时器

除了一次性定时器,你还可以使用周期性定时器来实现重复触发的任务。例如,如果你需要每 1 秒钟执行一次任务,可以设置一个周期性定时器。

周期性定时器实现

void periodic_timer_callback(struct timer_list *t)
{
    pr_info("Periodic Timer expired at jiffies: %ld\n", jiffies);

    // 重新启动定时器,设置周期为 1 秒
    mod_timer(t, jiffies + msecs_to_jiffies(1000));
}

my_module_init 中初始化周期性定时器:

static int __init my_module_init(void)
{
    pr_info("Module initialized\n");

    // 初始化定时器
    timer_setup(&my_timer, periodic_timer_callback, 0);

    // 启动定时器,1秒后首次触发
    mod_timer(&my_timer, jiffies + msecs_to_jiffies(1000));

    return 0;
}

这样,定时器将每 1 秒触发一次,直到模块被卸载。

5. 总结

  • timer_list 是 Linux 内核中实现定时器功能的核心数据结构。你可以用它来实现定时任务、超时机制以及周期性任务。
  • 定时器回调函数 在定时器到期时被调用,它通常用于处理延时任务或超时操作。
  • mod_timer 用于启动或重设定时器。
  • del_timer_sync 用于删除定时器,确保删除过程中不会再触发回调函数。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值