一、前言 #
内核里面计时使用的是jiffies变量,对用户态就是获取系统启动时间。获取系统启动时间也是根据jiffies进行换算得来。这里介绍一下内核里面的jiffies和计时系统。
二、使用实例 #
1. timer_list&jiffies 定时器使用 #
1#include <linux/jiffies.h>
2#include <linux/timer.h>
3
4static int toav6_expire_time = 5 * 60; // 默认过期时间5分钟
5module_param(toav6_expire_time, int, S_IRUGO);
6#define EXPIRE_TIME_JIFFIES (msecs_to_jiffies(toav6_expire_time * 1000))
7#define TIMER_INTERVAL EXPIRE_TIME_JIFFIES // 使用过期时间作为定时器间隔
8
9static struct timer_list toa_timer;
10
11static void toa_timer_callback(struct timer_list *timer) {
12 // do something
13
14 // Re-arm the timer
15 mod_timer(timer, jiffies + TIMER_INTERVAL);
16}
17
18bool toa_timer_init(void) {
19 static const char *tag = __FUNCTION__;
20 LOG_INFO("%s, init timer, intervel %d s", tag, toav6_expire_time);
21 // Initialize the timer
22 timer_setup(&toa_timer, toa_timer_callback, 0);
23
24 // Set the timer to fire in 5 minutes
25 return mod_timer(&toa_timer, jiffies + TIMER_INTERVAL);
26}
27
28void toa_timer_exit(void) {
29 del_timer(&toa_timer);
30}
三、jiffies定义 #
头文件引用的是extern变量
1// include/linux/jiffies.h
2/*
3 * The 64-bit value is not atomic - you MUST NOT read it
4 * without sampling the sequence number in jiffies_lock.
5 * get_jiffies_64() will do this for you as appropriate.
6 */
7extern u64 __cacheline_aligned_in_smp jiffies_64;
8extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;
源文件里面定义对应的jiffies_64
1// kernel/time/timer.c
2__visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
3
4EXPORT_SYMBOL(jiffies_64);
在汇编代码里面将两个变量的地址指定为同一个,实际上就是同一个变量,可能在32位系统中使用的是低32位
// arch/x86/kernel/vmlinux.lds.S
jiffies = jiffies_64;
三、jiffies的更新 #
1// kernel/time/tick-sched.c
2
3/* 调用到此的堆栈
4tick_do_update_jiffies64(ktime_t now) (kernel/time/tick-sched.c:118)
5tick_do_update_jiffies64(ktime_t now) (kernel/time/tick-sched.c:57)
6tick_nohz_update_jiffies(ktime_t now) (kernel/time/tick-sched.c:634)
7tick_nohz_irq_enter() (kernel/time/tick-sched.c:1438)
8tick_irq_enter() (kernel/time/tick-sched.c:1455)
9sysvec_apic_timer_interrupt(struct pt_regs * regs) (arch/x86/kernel/apic/apic.c:1106)
10*/
11/*
12 * Must be called with interrupts disabled !
13 */
14static void tick_do_update_jiffies64(ktime_t now)
15{
16 unsigned long ticks = 1;
17 ktime_t delta, nextp;
18
19 /*
20 * 64bit can do a quick check without holding jiffies lock and
21 * without looking at the sequence count. The smp_load_acquire()
22 * pairs with the update done later in this function.
23 *
24 * 32bit cannot do that because the store of tick_next_period
25 * consists of two 32bit stores and the first store could move it
26 * to a random point in the future.
27 */
28 if (IS_ENABLED(CONFIG_64BIT)) {
29 if (ktime_before(now, smp_load_acquire(&tick_next_period)))
30 return;
31 } else {
32 unsigned int seq;
33
34 /*
35 * Avoid contention on jiffies_lock and protect the quick
36 * check with the sequence count.
37 */
38 do {
39 seq = read_seqcount_begin(&jiffies_seq);
40 nextp = tick_next_period;
41 } while (read_seqcount_retry(&jiffies_seq, seq));
42
43 if (ktime_before(now, nextp))
44 return;
45 }
46
47 /* Quick check failed, i.e. update is required. */
48 raw_spin_lock(&jiffies_lock);
49 /*
50 * Reevaluate with the lock held. Another CPU might have done the
51 * update already.
52 */
53 if (ktime_before(now, tick_next_period)) {
54 raw_spin_unlock(&jiffies_lock);
55 return;
56 }
57
58 write_seqcount_begin(&jiffies_seq);
59
60 delta = ktime_sub(now, tick_next_period);
61 if (unlikely(delta >= TICK_NSEC)) {
62 /* Slow path for long idle sleep times */
63 s64 incr = TICK_NSEC;
64
65 ticks += ktime_divns(delta, incr);
66
67 last_jiffies_update = ktime_add_ns(last_jiffies_update,
68 incr * ticks);
69 } else {
70 last_jiffies_update = ktime_add_ns(last_jiffies_update,
71 TICK_NSEC);
72 }
73
74 /* Advance jiffies to complete the jiffies_seq protected job */
75 // 在这里完成对jiffies_64的新增
76 jiffies_64 += ticks;
77
78 /*
79 * Keep the tick_next_period variable up to date.
80 */
81 nextp = ktime_add_ns(last_jiffies_update, TICK_NSEC);
82
83 if (IS_ENABLED(CONFIG_64BIT)) {
84 /*
85 * Pairs with smp_load_acquire() in the lockless quick
86 * check above and ensures that the update to jiffies_64 is
87 * not reordered vs. the store to tick_next_period, neither
88 * by the compiler nor by the CPU.
89 */
90 smp_store_release(&tick_next_period, nextp);
91 } else {
92 /*
93 * A plain store is good enough on 32bit as the quick check
94 * above is protected by the sequence count.
95 */
96 tick_next_period = nextp;
97 }
98
99 /*
100 * Release the sequence count. calc_global_load() below is not
101 * protected by it, but jiffies_lock needs to be held to prevent
102 * concurrent invocations.
103 */
104 write_seqcount_end(&jiffies_seq);
105
106 calc_global_load();
107
108 raw_spin_unlock(&jiffies_lock);
109 update_wall_time();
110}