Linux timer

本文详细介绍了ARMv8架构下的Timer工作原理,包括硬件寄存器配置、Kernel Timer的代码架构及其实现流程。深入解析了Local Timer和Broadcast Timer的工作方式,并对低精度Timer的数据结构及Timer Wheel的实现进行了阐述。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Timer of armv8

硬件原理

Arm Generic Timer:

  • System counter
  • Arm core local timer
  • Interrupt controller
    在这里插入图片描述
    说明:
  • System counter → 根据clk source的频率(例如qcom 的clk source
    19.2M,应讲该值配置为19.2M即1s的时间)进行计时,配置一般在ATF中。
  • Arm core local timer → 每一个core有一个local timer用来提供timer服务。armv8
    core进入idle时local timer,可以停止以省电。
  • Interrupt controller →timer到时,主要向arm core提供PPI中断.

寄存器配置

CNTFRQ_EL0, Counter-timer Frequency register
Purpose
This register is provided so that software can discover the frequency of the system counter. It must
be programmed with this value as part of system initialization. The value of the register is not
interpreted by hardware.
在这里插入图片描述
Bits [31:0]
Clock frequency. Indicates the system counter clock frequency, in Hz.
This field resets to an architecturally UNKNOWN value.

CNTPCT_EL0, Counter-timer Physical Count register
Purpose
在这里插入图片描述
Bits [63:0]
Physical count value.

  1. 已知CNTFRQ_EL0的frequency读取CNTPCT_EL0的cycles之后可以计算出时间。
  2. 转换后的纳秒数目 = cycles*(1/ frequency)* NSEC_PER_SEC
  3. 但kernel中不会带着小数点计算(一个cycle1/frequency代表的时间)
  4. 将cycles*(1/ frequency)* NSEC_PER_SEC转化为(cycles * mult )>> shift。即NSEC_PER_SEC/frequency表示为mult/2^shift。

CNTP_CTL_EL0, Counter-timer Physical Timer Control register
Purpose
Control register for the EL1 physical timer.
在这里插入图片描述
ISTATUS, bit [2]
The status of the timer. This bit indicates whether the timer condition is met:
0b0 Timer condition is not met.
0b1 Timer condition is met.
IMASK, bit [1]
Timer interrupt mask bit. Permitted values are:
0b0 Timer interrupt is not masked by the IMASK bit.
0b1 Timer interrupt is masked by the IMASK bit.
ENABLE, bit [0]
Enables the timer. Permitted values are:
0b0 Timer disabled.
0b1 Timer enabled.

CNTP_CVAL_EL0, Counter-timer Physical Timer CompareValue register
Purpose
Holds the compare value for the EL1 physical timer.
在这里插入图片描述
CompareValue, bits [63:0]
Holds the EL1 physical timer CompareValue.
When CNTP_CTL_EL0.ENABLE is 1, the timer condition is met when (CNTPCT_EL0 -
CompareValue) is greater than or equal to zero. This means that CompareValue acts like a 64-bit
upcounter timer. When the timer condition is met:
• CNTP_CTL_EL0.ISTATUS is set to 1.
• If CNTP_CTL_EL0.IMASK is 0, an interrupt is generated.

CNTP_TVAL_EL0, Counter-timer Physical Timer TimerValue register
Purpose
Holds the timer value for the EL1 physical timer.
在这里插入图片描述
TimerValue, bits [31:0]
The TimerValue view of the EL1 physical timer.
On a read of this register:
• If CNTP_CTL_EL0.ENABLE is 0, the value returned is UNKNOWN .
• If CNTP_CTL_EL0.ENABLE is 1, the value returned is (CNTP_CVAL_EL0 -
CNTPCT_EL0).
On a write of this register, CNTP_CVAL_EL0 is set to (CNTPCT_EL0 + TimerValue), where
TimerValue is treated as a signed 32-bit integer.
When CNTP_CTL_EL0.ENABLE is 1, the timer condition is met when (CNTPCT_EL0 -
CNTP_CVAL_EL0) is greater than or equal to zero. This means that TimerValue acts like a 32-bit
downcounter timer. When the timer condition is met:
• CNTP_CTL_EL0.ISTATUS is set to 1.
• If CNTP_CTL_EL0.IMASK is 0, an interrupt is generated.

Kernel timer

代码架构

在这里插入图片描述

Local 和broadcast timer

在这里插入图片描述

  • Local timer: per cpu timer. core运行的时候,如果有到时timer,local timer会通过PPI中断通知core相应timer中断。
  • Global hw timer(broadcast timer): 当core进入idle状态的时候,为了省电可能会将local timer停止,这个时候讲这些停止local timer的core将最近的timer挂在broadcast timer上。一旦这个timer时间到了broadcast timer就会发SPI中断(IPI timer)给对应的core。

名词解释

  • Clock source:就是用来抽象一个在指定输入频率的clock下工作的一个counter.设备对应硬件的system free
    running counter,提供了一个基础的timeline. Clock event:是在timeline上指定的点产生clock
    event的设备, tick device可以注册成clockevent设备,并可以工作在periodic mode或者oneshot
    mod.
  • Periodic mode:用于低精度timer。周期模式,周期为jiffies(例HZ-250,jiffies
    4ms),就是每个jiffies产生一个PPI中断来处理timer。 Oneshut
    mode:非周期模式,根据注册的timer的expires时间来触发PPI中断。
  • NOHZ(tickless):这种模式下不在使用Periodic mode的中断,一般timer运行在高精度模式的时候会设置为NOHZ
  • 低精度timer: core运行的时候,如果有到时timer,local timer会通过PPI中断通知core相应timer中断。
  • 高精度timer(hrtimer):每个低精度timer中断处理的时候会检查是否可以进入高精度模式。进入该模式之后会进入NOHZ模式,且为oneshut mode。

低精度timer

数据结构

struct timer_list {
  /*
    * All fields that change during normal runtime grouped to the
    * same cacheline
  */
  struct hlist_node entry;
  unsigned long   expires;
  void      (*function)(struct timer_list *);
  u32     flags;
 
#ifdef CONFIG_LOCKDEP
  struct lockdep_map  lockdep_map;
#endif
};

代表定时器:

  1. entry:所有的定时器都会根据到期的时间被分配到一组链表中的一个中,该字段是链表的节点成员.
  2. expires:字段指出了该定时器的到期时刻,也就是期望定时器到期时刻的jiffies计数值。这是一个绝对值,不是距离当前时刻再过多少jiffies。
  3. function:是一个回调函数指针,定时器到期时,系统将会调用该函数,用于响应该定时器的到期事件。
  4. flags:看名字应该是标志位,其定义如下:
    #define TIMER_CPUMASK 0x0003FFFF 而最低的18位指示了该定时器绑定到了哪个CPU上,注意是一个数值,而不是位图。夹在中间的一些位到真的是一些标志位。
#define TIMER_MIGRATING   0x00040000 表示定时器正在从一个CPU迁移到另外一个CPU
#define TIMER_DEFERRABLE  0x00080000  表示该定时器是可延迟的
#define TIMER_PINNED    0x00100000 表示定时器已经绑死了当前的CPU,无论如何都不会迁移到别的CPU上 
#define TIMER_IRQSAFE   0x00200000 示定时器是中断安全的,使用的时候只需要加锁,不需要关中断.
最高10位记录了定时器放置到桶的编号。
struct timer_base {
    raw_spinlock_t    lock;
    struct timer_list *running_timer;
#ifdef CONFIG_PREEMPT_RT
    spinlock_t    expiry_lock;
    atomic_t    timer_waiters;
#endif
   unsigned long   clk;
   unsigned long   next_expiry;
   unsigned int    cpu;
   bool      next_expiry_recalc;
   bool      is_idle;
   DECLARE_BITMAP(pending_map, WHEEL_SIZE);
   struct hlist_head vectors[WHEEL_SIZE];
} ____cacheline_aligned; 

static DEFINE_PER_CPU(struct timer_base, timer_bases[NR_BASES]);

分为标准定时器和延时定时器,为per cpu的结构体,实现对定时器的管理:

  1. lock:保护该timer_base结构体的自旋锁。
  2. running_timer:该字段指向当前CPU正在处理的定时器所对应的timer_list结构。
  3. next_expiry:该字段指向该CPU下一个即将到期的定时器。最早 (距离超时最近的 timer) 的超时时间。
  4. cpu:所属的CPU号。
  5. is_idle:指示是否处于空闲模式下。
  6. must_forward_clk:指示是否需要更新当前clk的值
  7. pending_map:一个比特位图,时间轮中有几个桶就有几个比特位。如果某个桶内有定时器存在,那么就将相应的比特位置1。
  8. vectors:时间轮所有桶的数组,每一个元素是一个链表。
#ifdef CONFIG_NO_HZ_COMMON
#define NR_BASES 2
#define BASE_STD 0 //cpu idle的时候需要响应BASE_STD的timer
#define BASE_DEF 1 //cpu idle的时候不响需要应BASE_DEF的timer
#else
#define NR_BASES 1
#define BASE_STD 0                                                                 
#define BASE_DEF 0
#endif

Timer wheel在这里插入图片描述

  1. 1.首先支持NOHZ的cpu,每个cpu有两个timer base即BASE_STD和BASE_DEF。由于两个timer base结构一样,这里只说明BASE_STD(BASE_STD为标准的timer,BASE_DEF为可延迟的timer)。
  2. HZ 250为例分为9个level,每个level的有64个桶bucket。
  3. 同一个桶中挂的是expires时间相同的timer list

HZ 250

LevelOffsetGranularityRange
001 Tick-4 ms0 ms - 255 ms
1648 Ticks-32 ms256 ms - 2047 ms (256ms - ~2s)
212864 Ticks- 256 ms2048 ms - 16383 ms (~2s - ~16s)
3192512 Ticks-2048 ms (~2s)16384 ms - 131071 ms (~16s - ~2m)
42564096 Ticks-16384 ms (~16s)131072 ms - 1048575 ms (~2m - ~17m)
525632768 Ticks-131072 ms (~2m)1048576 ms - 8388607 ms (~17m - ~2h)
6384262144 Ticks-1048576 ms (~17m)8388608 ms - 67108863 ms (~2h - ~18h)
74482097152 Ticks- 8388608 ms (~2h)67108864 ms - 536870911 ms (~18h - ~6d)
851216777216 Ticks-67108864 ms (~18h)536870912 ms - 4294967288 ms (~6d - ~49d)
  1. 以上表格表示,会根据timer list的expires时间与当前时间的差值, 将timer list会挂在那个time。
  2. base的level中。 根据以上表格可以看到各个level的timer base精度不同。第一个level的精度为1个tick,第二个level的精度为8个tick,第三个level的精度为8的2次方个tick,以此类推。

实现

在这里插入图片描述

  1. 黄色的线代表kernel start flow,蓝色的线代表periodic flow,橘色的线代表hrtime flow,绿色的线代表local timer flow,黑色的线代表ppi irq。
  2. init timer的时候通过调init_timer_cpus用初始化了per cpu的timer base,并且注册了timer softirq handler函数为run_timer_softirq。
  3. local timer注册了ppi中断,并且将自己注册成clock_event_device,由于clock_event_device支持periodic mode和hrtimer mode。ppi中断到来的时候,调用对应mode的event handler。
  4. 最终periodic mode和hrtimer mode都会调用update_process_timers函数,该函数调用run_local_timer函数,和大名鼎鼎的scheduler_tick函数。其中run_local_timer函数首先会尝试切换到hrtimer mode,切换到高精度的同时,会将periodic模式的变换为一个hrtimer的定时器sched_timer。然后根据jiffies对比BASE_STD和BASE_DEF的next_expiry情况来触发TIMER_SOFTIRQ。
  5. periodic mode的时候clock_event_device的event handler为tick_handler_periodic会通过tick_periodic调用update_process_timers。
  6. hrtimer mode的时候clock_event_device的event handler为hrtimer_interrupt。hrtimer_interrupt会处理超时的hrtimer定时器,包括sched_timer。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值