内核计时和定时器

一、前言 #

内核里面计时使用的是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}