linux 内核定时器

1、linux 内核定时器介绍

linux操作系统提供了一个内核定时器,可在未来的某个特定时间点调度执行某个函数,完成指定任务,linux内核定时器基于软中断实现,提供了一种异步处理的机制,用户通过设置将来某一时刻的滴答值来实现定时功能,时间计算依赖jiffies。

jiffies为Linux核心变数(unsigned long),它被用来记录系统自开机以来,已经过了多少tick。每发生一次timer interrupt,jiffies变数会被加一。值得注意的是,jiffies于系统开机时,并非初始化成零,而是被设为-300*HZ,即代表系统于开机五分钟后,jiffies便会溢位。那溢位怎么办?事实上,Linux核心定义几个macro(timer_after、time_after_eq、time_before与time_before_eq),即便是溢位,也能借由这几个macro正确地取得jiffies的内容。

2、内核定时器执行过程

内核在时钟中断发生后执行定时器,定时器作为软中断在下半部上下文中执行。具体来说,时钟中断处理程序timer_interrupt(int irq, void *dummy)会执行update_process_times()函数,该函数随即调用run_local_timers()函数:

void run_local_timers(void)
{
    struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);

    hrtimer_run_queues();
    /* Raise the softirq only if required. */
    if (time_before(jiffies, base->clk)) {
        if (!IS_ENABLED(CONFIG_NO_HZ_COMMON))
            return;
        /* CPU is awake, so check the deferrable base. */
        base++;
        if (time_before(jiffies, base->clk))
            return;
    }
    raise_softirq(TIMER_SOFTIRQ);
}
run_local_timers()函数处理软中断TIMER_SOFTIRQ,从而在当前处理器上运行的所有的(如果有的话)超时定时器。
定时器的软中断处理程序为:run_timer_softirq()在/kernel/timer.c中定时器初始化时赋值:
void __init init_timers(void)
{
    init_timer_cpus();
    open_softirq(TIMER_SOFTIRQ, run_timer_softirq);
}
看一下定时器的软中断处理程序的实现:
static __latent_entropy void run_timer_softirq(struct softirq_action *h)
{
    struct timer_base *base = this_cpu_ptr(&timer_bases[BASE_STD]);

    /*
     * must_forward_clk must be cleared before running timers so that any
     * timer functions that call mod_timer will not try to forward the
     * base. idle trcking / clock forwarding logic is only used with
     * BASE_STD timers.
     *
     * The deferrable base does not do idle tracking at all, so we do
     * not forward it. This can result in very large variations in
     * granularity for deferrable timers, but they can be deferred for
     * long periods due to idle.
     */
    base->must_forward_clk = false;

    __run_timers(base);
    if (IS_ENABLED(CONFIG_NO_HZ_COMMON))
        __run_timers(this_cpu_ptr(&timer_bases[BASE_DEF]));
}
继续看__run_timers的实现:
static inline void __run_timers(struct timer_base *base)
{
    struct hlist_head heads[LVL_DEPTH];
    int levels;

    if (!time_after_eq(jiffies, base->clk))
        return;

    raw_spin_lock_irq(&base->lock);

    while (time_after_eq(jiffies, base->clk)) {

        levels = collect_expired_timers(base, heads);
        base->clk++;

        while (levels--)
            expire_timers(base, heads + levels);
    }
    base->running_timer = NULL;
    raw_spin_unlock_irq(&base->lock);
}
继续看expire_timers的实现:
static void expire_timers(struct timer_base *base, struct hlist_head *head)
{
    while (!hlist_empty(head)) {
        struct timer_list *timer;
        void (*fn)(unsigned long);
        unsigned long data;

        timer = hlist_entry(head->first, struct timer_list, entry);

        base->running_timer = timer;
        detach_timer(timer, true);

        fn = timer->function;
        data = timer->data;

        if (timer->flags & TIMER_IRQSAFE) {
            raw_spin_unlock(&base->lock);
            call_timer_fn(timer, fn, data);
            raw_spin_lock(&base->lock);
        } else {
            raw_spin_unlock_irq(&base->lock);
            call_timer_fn(timer, fn, data);
            raw_spin_lock_irq(&base->lock);
        }
    }
}
继续看call_timer_fn的实现:
static void call_timer_fn(struct timer_list *timer, void (*fn)(unsigned long),
              unsigned long data)
{
    int count = preempt_count();

#ifdef CONFIG_LOCKDEP
    /*
     * It is permissible to free the timer from inside the
     * function that is called from it, this we need to take into
     * account for lockdep too. To avoid bogus "held lock freed"
     * warnings as well as problems when looking into
     * timer->lockdep_map, make a copy and use that here.
     */
    struct lockdep_map lockdep_map;

    lockdep_copy_map(&lockdep_map, &timer->lockdep_map);
#endif
    /*
     * Couple the lock chain with the lock chain at
     * del_timer_sync() by acquiring the lock_map around the fn()
     * call here and in del_timer_sync().
     */
    lock_map_acquire(&lockdep_map);

    trace_timer_expire_entry(timer);
    fn(data);
    trace_timer_expire_exit(timer);

    lock_map_release(&lockdep_map);

    if (count != preempt_count()) {
        WARN_ONCE(1, "timer: %pF preempt leak: %08x -> %08x\n",
              fn, count, preempt_count());
        /*
         * Restore the preempt count. That gives us a decent
         * chance to survive and extract information. If the
         * callback kept a lock held, bad luck, but not worse
         * than the BUG() we had.
         */
        preempt_count_set(count);
    }
}
 

虽然所有的定时器都以链表的形式放在一起,但是让内核经常为了寻找超时定时器而遍历整个链表是不明智的。同样,将链表以超时时间进行排序也是不明智的做法,因为这样一来在链表中删除和插入定时器都会很费时。为了提高搜索效率,内核实现了时间轮算法,因此定时器管理代码是非常高效的。具体实现请查阅相关资料,在此不深入研究。

系统中所有的定时器的组织结构如下图所示:

3、定时器使用

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

#ifdef CONFIG_LOCKDEP
    struct lockdep_map  lockdep_map;
#endif
};

定时器使用过程:
1.定义一个定时器
2.初始化定时器
3.编写定时器处理函数
4.填充定时器结构体
5.启动定时器
6.更新定时器
7.删除定时器

定时器使用的函数:

1、定义定时器变量

struct timer_list timer;

2、初始化定时器

void init_timer(struct timer_list *timer);
新版本内核:
#define init_timer(timer)                       \
    __init_timer((timer), 0)
#define init_timer_pinned(timer)                    \
    __init_timer((timer), TIMER_PINNED)
#define init_timer_deferrable(timer)                    \
    __init_timer((timer), TIMER_DEFERRABLE)
#define init_timer_pinned_deferrable(timer)             \
    __init_timer((timer), TIMER_DEFERRABLE | TIMER_PINNED)
#define init_timer_on_stack(timer)                  \
    __init_timer_on_stack((timer), 0)

TIMER_DEFERRABLE:表示该定时器是可延迟的。
TIMER_PINNED:表示定时器已经绑死了当前的CPU,无论如何都不会迁移到别的CPU上。

3、添加定时器。定时器开始计时
void add_timer(struct timer_list * timer);  //定时器响应运行在申请的cpu上
void add_timer_on(struct timer_list *timer, int cpu) //定时器响应运行在指定的cpu上

4、删除定时器,在定时器到期前禁止一个已注册定时器
int del_timer(struct timer_list * timer);

5、如果定时器函数正在执行则在函数执行完后返回(SMP)
int del_timer_sync(struct timer_list *timer);

6、更新定时器到期时间,并开启定时器
int mod_timer(struct timer_list *timer,unsigned long expires);
新版本内核打开CONFIG_NO_HZ_COMMON和CONFIG_SMP有可能发生定时器漂移到其他cpu上,使用init_timer_pinned初始化定时器则不会。
7、查看定时器是否正在等待被调度运行
int timer_pending(const struct timer_list*timer);
/*返回值为真表示正在等待被调度运行*/

8、内核提供了四个宏来比较节拍计数,这些宏定义在文件<linux/jiffies.h>中:
#define time_after(a,b)     \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(b) - (long)(a) < 0))
#define time_before(a,b)    time_after(b,a)

#define time_after_eq(a,b)  \
    (typecheck(unsigned long, a) && \
     typecheck(unsigned long, b) && \
     ((long)(a) - (long)(b) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
64位:
    time_before64(unknown, known)
    time_after64(unknown, known)
    time_before_eq64(unknown, known)
    time_after_eq64(unknown, known)

比较的时候用这些宏可以避免jiffies由于过大造成的回绕问题。

拓展宏
/*
 * Calculate whether a is in the range of [b, c].
 */
#define time_in_range(a,b,c) \
    (time_after_eq(a,b) && \
     time_before_eq(a,c))

/*
 * Calculate whether a is in the range of [b, c).
 */
#define time_in_range_open(a,b,c) \
    (time_after_eq(a,b) && \
     time_before(a,c))

/* time_is_before_jiffies(a) return true if a is before jiffies */
#define time_is_before_jiffies(a) time_after(jiffies, a)

/* time_is_after_jiffies(a) return true if a is after jiffies */
#define time_is_after_jiffies(a) time_before(jiffies, a)

/* time_is_before_eq_jiffies(a) return true if a is before or equal to jiffies*/
#define time_is_before_eq_jiffies(a) time_after_eq(jiffies, a)

/* time_is_after_eq_jiffies(a) return true if a is after or equal to jiffies*/
#define time_is_after_eq_jiffies(a) time_before_eq(jiffies, a)


对于time_after等比较jiffies先/后的宏,两个值的取值应当满足以下限定条件:
1)    两个值之间相差从逻辑值来讲应小于有符号整型的最大值。
对于32位无符号整型,两个值之间相差从逻辑值来讲应小于2147483647。
对于HZ=100,那么两个时间值之间相差不应当超过2147483647/100秒 = 0.69年 = 248.5天。对于HZ=60,那么两个时间值之间相差不应当超过2147483647/60秒 = 1.135年。在实际代码应用中,需要比较先/后的两个时间值之间一般都相差很小,范围大致在1秒~1天左右,所以以上time_after等比较时间先/后的宏完全可以放心地用于实际的代码中。
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

一叶知秋yyds

分享是一种美德,感谢金主打赏

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值