Timer源代码分析

Timer源代码分析u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;EXPORT_SYMBOL(jiffies_64); /* * Have the 32 bit jiffies value wrap 5 minutes after boot * so jiffies wrap bugs show up
摘要由CSDN通过智能技术生成

Timer源代码分析

u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;

EXPORT_SYMBOL(jiffies_64);

 

/*

 * Have the 32 bit jiffies value wrap 5 minutes after boot

 * so jiffies wrap bugs show up earlier.

 */

#define INITIAL_JIFFIES ((unsigned long)(unsigned int) (-300*HZ))

 

一般在嵌入式的平台上,HZ取值为100,所以300*HZ的值为30000。30000的十六进制表示为0x7530。数据都是以补码的形式存储的,正数的补码是它本身,负数的补码是取反再加1。0x7530取反后是0xffff8acf,再加1后是0xffff8ad0。由于经过了强制类型转换(unsigned long)(unsigned int),所以jiffies_64被赋值为0xffff8ad0

 

/*

 * per-CPU timer vector definitions:

 */

#define TVN_BITS (CONFIG_BASE_SMALL ? 4 : 6)

#define TVR_BITS (CONFIG_BASE_SMALL ? 6 : 8)

#define TVN_SIZE (1 << TVN_BITS)

#define TVR_SIZE (1 << TVR_BITS)

#define TVN_MASK (TVN_SIZE - 1)

#define TVR_MASK (TVR_SIZE - 1)

 

struct tvec {

                        struct list_head vec[TVN_SIZE];

};

 

struct tvec_root {

                        struct list_head vec[TVR_SIZE];

};

 

struct tvec_base {

                        spinlock_t lock;

                        struct timer_list *running_timer;

                        unsigned long timer_jiffies;

                        unsigned long next_timer;

                        struct tvec_root tv1;

                        struct tvec tv2;

                        struct tvec tv3;

                        struct tvec tv4;

                        struct tvec tv5;

} ____cacheline_aligned;

 

struct tvec_base boot_tvec_bases;

EXPORT_SYMBOL(boot_tvec_bases);

static DEFINE_PER_CPU(struct tvec_base *, tvec_bases) = &boot_tvec_bases;

 

这段代码是per-CPU定时器矢量的定义。

    一般在内存比较少的平台才会定义CONFIG_BASE_SMALL为1,以便缩减内存的占用。在我们的平台上,CONFIG_BASE_SMALL定义为0。所以TVN_BITS为6,TVR_BITS为8,TVN_SIZE为26 =64,TVN_SIZE为28=256,TVN_MASK为0X3F,TVR_MASK为0XFF。

struct tvec 是一个有64个链表头的数组,struct tvec_base是一个有256个链表头的数组。

struct tvec_base结构体中:

lock是个自旋锁,操作struct tvec_base结构体中的变量值时都必须持有锁,以免引起同步等问题。

running_timer是本地cpu正在运行的定时器。

timer_jiffies 表示需要检查的动态定时器的最早到期时间。该字段在系统启动时被设置成jiffies的值,且只能由run_timer_softirq()函数增加它的值。一般情况下,它的值bijiffies的值大1。

next_timer是本地cpu最早到期的定时器。

tv1是包含256个list_head元素的数组,包含了在28-1个节拍内将要到期的所有动态定时器。

tv2是包含64个list_head元素的数组,包含了在214-1个节拍内将要到期的所有动态定时器。

tv3是包含64个list_head元素的数组,包含了在220-1个节拍内将要到期的所有动态定时器。

tv4是包含64个list_head元素的数组,包含了在226-1个节拍内将要到期的所有动态定时器。

tv5 与前面的字段几乎相同,但唯一的区别就是vec数组的最后一项是一个大expires字段值的动态定时器。

 

 

/* Functions below help us manage 'deferrable' flag */

static inline unsigned int tbase_get_deferrable(struct tvec_base *base)

{

                   return ((unsigned int)(unsigned long)base & TBASE_DEFERRABLE_FLAG);

}

 

static inline struct tvec_base *tbase_get_base(struct tvec_base *base)

{

                   return ((struct tvec_base *)((unsigned long)base & ~TBASE_DEFERRABLE_FLAG));

}

 

static inline void timer_set_deferrable(struct timer_list *timer)

{

                   timer->base = TBASE_MAKE_DEFERRED(timer->base);

}

 

static inline void

timer_set_base(struct timer_list *timer, struct tvec_base *new_base)

{

                   timer->base = (struct tvec_base *)((unsigned long)(new_base) |

                                                                                                 tbase_get_deferrable(timer->base));

}

 

/*

 * Note that all tvec_bases are 2 byte aligned and lower bit of

 * base in timer_list is guaranteed to be zero. Use the LSB to

 * indicate whether the timer is deferrable.

 *

 * A deferrable timer will work normally when the system is busy, but

 * will not cause a CPU to come out of idle just to service it; instead,

 * the timer will be serviced when the CPU eventually wakes up with a

 * subsequent non-deferrable timer.

 */

#define TBASE_DEFERRABLE_FLAG                                     (0x1)

 

刚看这段代码时看得一头雾水,竟然绕个弯来判断定时器是否是可延迟的。它的原理是这样的:tvec_bases这个数据结构是2个字节对齐的,而timer_list结构体的base字段就是指向tvec_base的地址,所以base的值肯定是2的倍数,即最低位为0。搞不明白为什么要这样设计,增加代码复杂性,难道仅仅是为了省个保存标志位的内存?

 

#define TBASE_MAKE_DEFERRED(ptr) ((struct tvec_base *)                             \

                                             ((unsigned char *)(ptr) + TBASE_DEFERRABLE_FLAG))

这个宏定义的作用是将原来的地址加1,即如果原来timer_list的base字段的地址为0x11223344,调用timer_set_deferrable(struct timer_list *timer)之后timer->base的值变为0x11223345。

 

static unsigned long round_jiffies_common(unsigned long j, int cpu,

                                           bool force_up)

{

                   int rem;

                   unsigned long original = j;

 

                   /*

                    * We don't want all cpus firing their timers at once hitting the

                    * same lock or cachelines, so we skew each extra cpu with an extra

                    * 3 jiffies. This 3 jiffies came originally from the mm/ code which

                    * already did this.

                    * The skew is done by adding 3*cpunr, then round, then subtract this

                    * extra offset again.

                    */

                   j += cpu * 3;

 

                   rem = j % HZ;

 

                   /*

                    * If the target jiffie is just after a whole second (which can happen

                    * due to delays of the timer irq, long irq off times etc etc) then

                    * we should round down to the whole second, not up. Use 1/4th second

                    * as cutoff for this rounding as an extreme upper bound for this.

                    * But never round down if @force_up is set.

                    */

                   if (rem < HZ/4 && !force_up) /* round down */

                           

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
timer_interrupt() 是 Linux 内核中的一个定时器中断处理函数,它主要用于处理内核中的各种定时器事件,包括时钟中断、定时器任务等。 源码如下: ```c void __irqentry timer_interrupt(struct pt_regs *regs) { ... update_process_times(user_mode(regs)); profile_tick(CPU_PROFILING, user_mode(regs), regs); if (user_mode(regs)) return; irq_enter(); #ifdef CONFIG_NO_HZ_COMMON /* * The tick broadcast device is disabled after the first * CPU goes offlined, see tick_nohz_enable. */ if (tick_nohz_tick_stopped()) goto out; #endif tick_check_idle(TICK_NMI_SAFE); /* * Tickless idle is in progress. */ if (idle_cpu(smp_processor_id())) { watchdog_touch(); smp_idle_check(); goto out; } /* * Check if we need to do anything at all: */ if (!tick_check_oneshot_broadcast(tick_nohz_full_cpu_idle())) { if (tick_check_oneshot() && !tick_check_broadcast_expired() && !tick_broadcast_oneshot_active()) { tick_program_event(tick_oneshot_broadcast, oneshot_timer.expires); goto out; } if (tick_check_broadcast_spurious()) goto out; if (tick_check_cpu_dead(cpu) || tick_check_new_device(cpu)) goto out; tick_check_replacement(cpu); } /* * Re-enable periodic tick if it is stopped and there are no * oneshot or broadcast events pending: */ if (tick_check_periodic() && !tick_check_oneshot_active() && !tick_check_broadcast_active()) tick_program_event(tick_periodic, tick_next_period); out: irq_exit(); ... } ``` 该函数的主要流程如下: 1. 调用 update_process_times() 和 profile_tick() 更新进程的时间信息和性能分析信息。 2. 判断是否是用户态,如果是则直接返回。 3. 调用 irq_enter() 进入中断上下文。 4. 检查 tickless idle 是否正在进行,如果是,则直接返回。 5. 检查是否正在进行 idle,如果是,则调用 watchdog_touch() 和 smp_idle_check(),并直接返回。 6. 检查是否需要进行任何操作。 7. 如果需要,检查是否需要启动一次性定时器事件。 8. 如果需要,检查是否需要启动广播定时器事件。 9. 如果需要,检查是否需要停止定时器,并重新启动。 10. 调用 irq_exit() 退出中断上下文。 总的来说,timer_interrupt() 函数主要用于检查和处理各种定时器事件,以保证内核的正常运行。这些事件包括一次性定时器、广播定时器、周期性定时器等。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值