linux时间子系统 - 周期性任务

来源:互联网 发布:python 语音处理 编辑:程序博客网 时间:2024/04/29 02:22

如果内核是有生命的话,那么时间就是内核的心脏,控制着内核的脉搏,但是这颗心脏跳动的方式根据硬件的配置会有不同的跳动方式。内核中有大量的需求需要时间的帮助,比如:定时、进程调度、获得时间等等,在内核中时间子系统就是来实现这部分功能的,根据不同的工作模式(periodic和oneshot)会有不同的工作函数来实现周期性任务,具体分为低精度模式和高精度模式。

1. 低精度模式


1.1 tick_handle_periodic

tick_handle_periodic函数的调用图如下:

这里写图片描述

此函数主要是更新jiffies_64、计算负载、更新墙上时间也就是系统时间,由于是工作在periodic模式,所以每次执行完毕,没必要reprogram下一次的event。

下面再介绍一下update_process_times

1.2 update_process_times

这里写图片描述

A : 从periodic转换到oneshot模式(动态模式),分为低精度和高精度
B:调度相关操作
C:posix timer相关操作

2. 高精度模式


2.1 高精度周期性任务的注册

在高精度模式下,时钟的轮转都是动态的,所以要执行周期性的任务需要基于动态时钟来模拟周期时钟,高精度时钟是基于hrtimer实现的,所以周期性的任务被初始化为一个hrtimer并被注册。

这里写图片描述

A : 更换要调用的函数为hrtimer_interrupt
B : 设置周期性的hrtimer

2.2 hrtimer_interrupt

这里写图片描述

hrtimer_interrupt做的主要工作是:
1.得到此时的time_now
2.运行hrtimer队列中的任务
3.设置下一次中断的时间

__hrtimer_run_queues主要从队列中得到合适的函数来执行

(kernel/time/hrtimer.c)

static void __hrtimer_run_queues(struct hrtimer_cpu_base *cpu_base, ktime_t now){    struct hrtimer_clock_base *base = cpu_base->clock_base;    unsigned int active = cpu_base->active_bases;    for (; active; base++, active >>= 1) {        struct timerqueue_node *node;        ktime_t basenow;        if (!(active & 0x01))            continue;        basenow = ktime_add(now, base->offset);        while ((node = timerqueue_getnext(&base->active))) {--------------得到下一个hrtimer            struct hrtimer *timer;            timer = container_of(node, struct hrtimer, node);            /*             * The immediate goal for using the softexpires is             * minimizing wakeups, not running timers at the             * earliest interrupt after their soft expiration.             * This allows us to avoid using a Priority Search             * Tree, which can answer a stabbing querry for             * overlapping intervals and instead use the simple             * BST we already have.             * We don't add extra wakeups by delaying timers that             * are right-of a not yet expired timer, because that             * timer will have to trigger a wakeup anyway.             */            if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer))                break;            __run_hrtimer(cpu_base, base, timer, &basenow);-------------运行hrtimer        }    }}

不过有一点还没说清楚,hrtimer_interrupt是和tick_handle_periodic一样执行周期任务的,但是从hrtimer_interrupt中只能看到它会调用相关的function,没有看到tick_periodic相关的操作。这是因为在切换到oneshot的时候,已经把相关的hrtimer注册到其中了,具体下节2.2介绍

2.3 tick_setup_sched_timer

在1.2节中对于模式的切换有一个函数没有太多介绍,这个函数就是tick_setup_sched_timer,它的主要工作就是完成执行类似tick_periodic相关操作函数的初始化,把其加入到hrtimer_cpu_base中

( kernel/time/tick-sched.c )

void tick_setup_sched_timer(void){    struct tick_sched *ts = this_cpu_ptr(&tick_cpu_sched);    ktime_t now = ktime_get();    /*     * Emulate tick processing via per-CPU hrtimers:     */    hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);--把sched_timer这个hrtimer加入到hrtimer_cpu_base中    ts->sched_timer.function = tick_sched_timer;------------------------周期性调用的函数(类似tick_periodic)    /* Get the next period (per cpu) */    hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());    /* Offset the tick to avert jiffies_lock contention. */    if (sched_skew_tick) {        u64 offset = ktime_to_ns(tick_period) >> 1;        do_div(offset, num_possible_cpus());        offset *= smp_processor_id();        hrtimer_add_expires_ns(&ts->sched_timer, offset);    }    hrtimer_forward(&ts->sched_timer, now, tick_period);    hrtimer_start_expires(&ts->sched_timer, HRTIMER_MODE_ABS_PINNED);    tick_nohz_activate(ts, NOHZ_MODE_HIGHRES);

2.4 tick_sched_timer

static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer){    struct tick_sched *ts =        container_of(timer, struct tick_sched, sched_timer);    struct pt_regs *regs = get_irq_regs();    ktime_t now = ktime_get();    tick_sched_do_timer(now);----------------------更新jiffies64    /*     * Do not call, when we are not in irq context and have     * no valid regs pointer     */    if (regs)        tick_sched_handle(ts, regs);    /* No need to reprogram if we are in idle or full dynticks mode */    if (unlikely(ts->tick_stopped))        return HRTIMER_NORESTART;    hrtimer_forward(timer, now, tick_period);-----    return HRTIMER_RESTART;}

change log

date content linux 2016.12.25 (圣诞节还在写博客) linux4.6.3 2017.1.2 分清高精度和低精度周期任务 linux4.6.3
0 1