Notes about hrtimers in Linux kernel.本文内容主要转载自Linux内核高精度定时器hrtimer 使用实例

1. Background

One might ask the question: we already have a timer subsystem (kernel/timers.c), why do we need two timer subsystems? After a lot of back and forth trying to integrate high-resolution and high-precision features into the existing timer framework, and after testing various such high-resolution timer implementations in practice, we came to the conclusion that the timer wheel code is fundamentally not suitable for such an approach. We initially didn’t believe this (‘there must be a way to solve this’), and spent a considerable effort trying to integrate things into the timer wheel, but we failed.

内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,以下学习使用hrtimer(high resolution timer)高精度定时器。

2. Interface

2.1 hrtimer_init

hrtimer_init函数初始化定时器工作模式。which_clock可以是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_BOOTTIME中的一种,mode则可以是相对时间HRTIMER_MODE_REL,也可以是绝对时间HRTIMER_MODE_ABS。

1
2
void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
enum hrtimer_mode mode);

2.2 设定超时回调函数

1
timer.function = hr_callback;

2.3 hrtimer_start

使用hrtimer_start激活该定时器。根据tim和mode参数的值计算hrtimer的超时时间,并设置到timer->expire域。 expire设置的是绝对时间,所以如果参数mode的值为HRTIMER_MODE_REL(即参数tim的值为相对时间),那么需要将tim的值修正为绝对时间:expire = tim + timer->base->get_time(),调用enqueue_hrtimer,将hrtimer加入到红黑树中。

1
2
int hrtimer_start(struct hrtimer *timer, ktime_t tim,
const enum hrtimer_mode mode);

2.4 hrtimer_cancel

使用hrtimer_cancel取消一个hrtimer。

1
int hrtimer_cancel(struct hrtimer *timer);

2.5 function字段指定的回调函数返回值

定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活。

1
2
3
4
enum hrtimer_restart {
HRTIMER_NORESTART, /* Timer is not restarted */
HRTIMER_RESTART, /* Timer must be restarted */
};

2.6 hrtimer_forward

把hrtimer的到期时间推进一个tick周期,返回HRTIMER_RESTART表明该hrtimer需要再次启动,以便产生下一个tick事件。

1
2
3
4
	hrtimer_forward(timer, now, tick_period);

return HRTIMER_RESTART;
}

3. Example

Simple example, callback every 100ms:

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/kernel.h>
#include <linux/module.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>

static struct hrtimer test_hrtimer;
static u64 sampling_period_ms = 100; // 100ms
static u32 loop = 0;

static enum hrtimer_restart test_hrtimer_handler(struct hrtimer *timer)
{
pr_info("test_hrtimer_handler: %u\n", ++loop);
hrtimer_forward_now(&test_hrtimer, ms_to_ktime(sampling_period_ms));
return HRTIMER_RESTART;
}

static int __init test_hrtimer_init(void)
{
hrtimer_init(&test_hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
test_hrtimer.function = &test_hrtimer_handler;
hrtimer_start(&test_hrtimer, ms_to_ktime(sampling_period_ms), HRTIMER_MODE_REL);
pr_info("init test_hrtimer.\n");

return 0;
}

static void __exit test_hrtimer_exit(void)
{
hrtimer_cancel(&test_hrtimer );
pr_info("exit test_hrtimer.\n");
return;
}

module_init(test_hrtimer_init);
module_exit(test_hrtimer_exit);

MODULE_LICENSE("GPL");

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ dmesg
[1213154.564771] init test_hrtimer.
[1213154.664771] test_hrtimer_handler: 1
[1213154.764769] test_hrtimer_handler: 2
[1213154.864768] test_hrtimer_handler: 3
[1213154.964766] test_hrtimer_handler: 4
[1213155.064765] test_hrtimer_handler: 5
[1213155.164763] test_hrtimer_handler: 6
[1213155.264761] test_hrtimer_handler: 7
[1213155.364759] test_hrtimer_handler: 8
[1213155.464758] test_hrtimer_handler: 9
[1213155.564756] test_hrtimer_handler: 10
[1213155.664755] test_hrtimer_handler: 11
[1213155.764753] test_hrtimer_handler: 12
[1213155.864752] test_hrtimer_handler: 13
[1213155.964750] test_hrtimer_handler: 14
[1213156.064749] test_hrtimer_handler: 15

参考资料:

  1. Linux内核高精度定时器hrtimer 使用实例
  2. hrtimers - subsystem for high-resolution kernel timers
  3. Using hrtimers in the Linux Kernel