我是靠谱客的博主 怡然鼠标,最近开发中收集的这篇文章主要介绍High-Resolution Timers,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

通常软时钟是建立在周期性的时钟中断的基础之上的,为了获取精度较高的软时钟中断,不得不提高时钟中断频率,但是过高的中断频率会造成CPU运算周期的浪费。High-Resolution机制,通过可编程的硬件定时器,把它的到期时间设置为软定时器队列中最早到期的时间,当时钟到期时,再把剩余的软定时器队列的最早到期时间编程到硬件定时器中,这样既能提高软时钟的精度,也不至于影响系统的性能。

High-Resolution Timers管理结构:

/**
 * struct hrtimer - the basic hrtimer structure
 * @node:	timerqueue node, which also manages node.expires,
 *		the absolute expiry time in the hrtimers internal
 *		representation. The time is related to the clock on
 *		which the timer is based. Is setup by adding
 *		slack to the _softexpires value. For non range timers
 *		identical to _softexpires.
 * @_softexpires: the absolute earliest expiry time of the hrtimer.
 *		The time which was given as expiry time when the timer
 *		was armed.
 * @function:	timer expiry callback function
 * @base:	pointer to the timer base (per cpu and per clock)
 * @state:	state information (See bit values above)
 * @start_site:	timer statistics field to store the site where the timer
 *		was started
 * @start_comm: timer statistics field to store the name of the process which
 *		started the timer
 * @start_pid: timer statistics field to store the pid of the task which
 *		started the timer
 *
 * The hrtimer structure must be initialized by hrtimer_init()
 */
struct hrtimer {
	struct timerqueue_node		node;  
	ktime_t				_softexpires;
	enum hrtimer_restart		(*function)(struct hrtimer *);
	struct hrtimer_clock_base	*base;
	unsigned long			state;
#ifdef CONFIG_TIMER_STATS
	int				start_pid;
	void				*start_site;
	char				start_comm[16];
#endif
};

struct timerqueue_node {
    struct rb_node node; //采用红黑树进行管理
    ktime_t expires; //定时器到期时间
};


/**
 * struct hrtimer_clock_base - the timer base for a specific clock
 * @cpu_base:        per cpu clock base
 * @index:        clock type index for per_cpu support when moving a
 *            timer to a base on another cpu.
 * @clockid:        clock id for per_cpu support
 * @active:        red black tree root node for the active timers
 * @resolution:        the resolution of the clock, in nanoseconds
 * @get_time:        function to retrieve the current time of the clock
 * @softirq_time:    the time when running the hrtimer queue in the softirq
 * @offset:        offset of this clock to the monotonic base
 */
struct hrtimer_clock_base {
    struct hrtimer_cpu_base    *cpu_base;
    int            index;
    clockid_t        clockid;
    struct timerqueue_head    active;
    ktime_t            resolution;
    ktime_t            (*get_time)(void);
    ktime_t            softirq_time;
    ktime_t            offset;
};


 

High-Resolution Timers的初始化:
void __init hrtimers_init(void)
{
	hrtimer_cpu_notify(&hrtimers_nb, (unsigned long)CPU_UP_PREPARE,
			  (void *)(long)smp_processor_id());
	register_cpu_notifier(&hrtimers_nb);
#ifdef CONFIG_HIGH_RES_TIMERS
	open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
#endif
}

//最终调用

/*
 * Functions related to boot-time initialization:
 */
static void __cpuinit init_hrtimers_cpu(int cpu)
{
    struct hrtimer_cpu_base *cpu_base = &per_cpu(hrtimer_bases, cpu);
    int i;

    raw_spin_lock_init(&cpu_base->lock);

    for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
        cpu_base->clock_base[i].cpu_base = cpu_base;
        timerqueue_init_head(&cpu_base->clock_base[i].active);
    }

    hrtimer_init_hres(cpu_base);
}

 
内核初始化之初,并没有启动High-Resolution Timers,每当执行时钟软中断时,会检查能否需要进入High-Resolution Timers模式,如果能,则进行进一步的初始化工作:


/*
 * This function runs timers and the timer-tq in bottom half context.
 */
static void run_timer_softirq(struct softirq_action *h)
{
    struct tvec_base *base = __this_cpu_read(tvec_bases);

    hrtimer_run_pending();

    if (time_after_eq(jiffies, base->timer_jiffies))
        __run_timers(base);
}



/*
 * Called from timer softirq every jiffy, expire hrtimers:
 *
 * For HRT its the fall back code to run the softirq in the timer
 * softirq context in case the hrtimer initialization failed or has
 * not been done yet.
 */
void hrtimer_run_pending(void)
{
    if (hrtimer_hres_active())  //如果已经启用了hrtimer机制,则返回。
        return;

    /*
     * This _is_ ugly: We have to check in the softirq context,
     * whether we can switch to highres and / or nohz mode. The
     * clocksource switch happens in the timer interrupt with
     * xtime_lock held. Notification from there only sets the
     * check bit in the tick_oneshot code, otherwise we might
     * deadlock vs. xtime_lock.
     */
    if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))  
 
    /*hrtimer_is_hres_enabled: query if the highres mode is enabled      
     * tick_check_oneshot_change: Check, if a change happened, which makes oneshot possible.    
     */ 

       hrtimer_switch_to_hres(); //Switch to high resolution mode
}


/*
 * Switch to high resolution mode
 */
static int hrtimer_switch_to_hres(void)
{
    int i, cpu = smp_processor_id();
    struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
    unsigned long flags;

    if (base->hres_active)  //如果已经启用了hres机制,则返回
        return 1;

    local_irq_save(flags);

    if (tick_init_highres()) {
        local_irq_restore(flags);
        printk(KERN_WARNING "Could not switch to high resolution "
                    "mode on CPU %dn", cpu);
        return 0;
    }
    base->hres_active = 1;
    for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
        base->clock_base[i].resolution = KTIME_HIGH_RES;

    tick_setup_sched_timer();  // setup the tick emulation timer

    /* "Retrigger" the interrupt to get things going */
    retrigger_next_event(NULL);
    local_irq_restore(flags);
    return 1;
}

/**
 * tick_init_highres - switch to high resolution mode
 *
 * Called with interrupts disabled.
 */
int tick_init_highres(void)
{
    return tick_switch_to_oneshot(hrtimer_interrupt);
}


/**
 * tick_switch_to_oneshot - switch to oneshot mode
 */
int tick_switch_to_oneshot(void (*handler)(struct clock_event_device *))
{
    struct tick_device *td = &__get_cpu_var(tick_cpu_device);
    struct clock_event_device *dev = td->evtdev;

    if (!dev || !(dev->features & CLOCK_EVT_FEAT_ONESHOT) ||
            !tick_device_is_functional(dev)) {

        printk(KERN_INFO "Clockevents: "
               "could not switch to one-shot mode:");
        if (!dev) {
            printk(" no tick devicen");
        } else {
            if (!tick_device_is_functional(dev))
                printk(" %s is not functional.n", dev->name);
            else
                printk(" %s does not support one-shot mode.n",
                       dev->name);
        }
        return -EINVAL;
    }

    td->mode = TICKDEV_MODE_ONESHOT;
/*
把CPU本地clock event device的event handler设置为hrtimer_interrupt()
*/
    dev->event_handler = handler;
    clockevents_set_mode(dev, CLOCK_EVT_MODE_ONESHOT);
    tick_broadcast_switch_to_oneshot();
    return 0;
}


/*
 * Select oneshot operating mode for the broadcast device
 */
void tick_broadcast_switch_to_oneshot(void)
{
    struct clock_event_device *bc;
    unsigned long flags;

    raw_spin_lock_irqsave(&tick_broadcast_lock, flags);
/*
把全局broadcast device的event_handler设置为tick_broadcast_setup_oneshot.de
*/

    tick_broadcast_device.mode = TICKDEV_MODE_ONESHOT;
    bc = tick_broadcast_device.evtdev;
    if (bc)
        tick_broadcast_setup_oneshot(bc);
    raw_spin_unlock_irqrestore(&tick_broadcast_lock, flags);
}

/*
*切换到High-Resolution Timers后,时钟中断设备主要不是为了周期性的tick而发出中断,所以每一个CPU有一个虚拟tick定时器。
*/

/**
 * struct tick_sched - sched tick emulation and no idle tick control/stats
 * @sched_timer:    hrtimer to schedule the periodic tick in high
 *            resolution mode
 * @idle_tick:        Store the last idle tick expiry time when the tick
 *            timer is modified for idle sleeps. This is necessary
 *            to resume the tick timer operation in the timeline
 *            when the CPU returns from idle
 * @tick_stopped:    Indicator that the idle tick has been stopped
 * @idle_jiffies:    jiffies at the entry to idle for idle time accounting
 * @idle_calls:        Total number of idle calls
 * @idle_sleeps:    Number of idle calls, where the sched tick was stopped
 * @idle_entrytime:    Time when the idle call was entered
 * @idle_waketime:    Time when the idle was interrupted
 * @idle_exittime:    Time when the idle state was left
 * @idle_sleeptime:    Sum of the time slept in idle with sched tick stopped
 * @iowait_sleeptime:    Sum of the time slept in idle with sched tick stopped, with IO outstanding
 * @sleep_length:    Duration of the current idle sleep
 * @do_timer_lst:    CPU was the last one doing do_timer before going idle
 */
struct tick_sched {
    struct hrtimer            sched_timer;
    unsigned long            check_clocks;
    enum tick_nohz_mode        nohz_mode;
    ktime_t                idle_tick;
    int                inidle;
    int                tick_stopped;
    unsigned long            idle_jiffies;
    unsigned long            idle_calls;
    unsigned long            idle_sleeps;
    int                idle_active;
    ktime_t                idle_entrytime;
    ktime_t                idle_waketime;
    ktime_t                idle_exittime;
    ktime_t                idle_sleeptime;
    ktime_t                iowait_sleeptime;
    ktime_t                sleep_length;
    unsigned long            last_jiffies;
    unsigned long            next_jiffies;
    ktime_t                idle_expires;
    int                do_timer_last;
};





/**
 * tick_setup_sched_timer - setup the tick emulation timer
 */
void tick_setup_sched_timer(void)
{
    struct tick_sched *ts = &__get_cpu_var(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);
    ts->sched_timer.function = tick_sched_timer;

    /* Get the next period (per cpu) */
    hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());

/*
*添加hrtimer
*/

    for (;;) {
        hrtimer_forward(&ts->sched_timer, now, tick_period);
        hrtimer_start_expires(&ts->sched_timer,
                      HRTIMER_MODE_ABS_PINNED);
        /* Check, if the timer was already in the past */
        if (hrtimer_active(&ts->sched_timer))
            break;
        now = ktime_get();
    }

#ifdef CONFIG_NO_HZ
    if (tick_nohz_enabled) {
        ts->nohz_mode = NOHZ_MODE_HIGHRES;
        printk(KERN_INFO "Switched to NOHz mode on CPU #%dn", smp_processor_id());
    }
#endif
}




/*
 * Called from hardirq context every jiffy
 */
void hrtimer_run_queues(void)
{
	struct timerqueue_node *node;
	struct hrtimer_cpu_base *cpu_base = &__get_cpu_var(hrtimer_bases);
	struct hrtimer_clock_base *base;
	int index, gettime = 1;
/*如果已经启用了hrtimer,则返回*/
	if (hrtimer_hres_active())
		return;
 
	for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) { //处理CPU上所有的hr_clock_base
		base = &cpu_base->clock_base[index];
		if (!timerqueue_getnext(&base->active))
			continue;

		if (gettime) {
			hrtimer_get_softirq_time(cpu_base);
			gettime = 0;
		}

		raw_spin_lock(&cpu_base->lock);

		while ((node = timerqueue_getnext(&base->active))) { //处理base上所有到期的定时器
			struct hrtimer *timer;

			timer = container_of(node, struct hrtimer, node);
			if (base->softirq_time.tv64 <=                     //如果发现一个定时器未到期,则其后的定时器肯定没有到期,则break
					hrtimer_get_expires_tv64(timer))
				break;

			__run_hrtimer(timer, &base->softirq_time);  //如果已经到期,则执行High-Resolution Timers的软中断函数
		}
		raw_spin_unlock(&cpu_base->lock);
	}
}


static void __run_hrtimer(struct hrtimer *timer, ktime_t *now)
{
    struct hrtimer_clock_base *base = timer->base;
    struct hrtimer_cpu_base *cpu_base = base->cpu_base;
    enum hrtimer_restart (*fn)(struct hrtimer *);
    int restart;

    WARN_ON(!irqs_disabled());

    debug_deactivate(timer);
    __remove_hrtimer(timer, base, HRTIMER_STATE_CALLBACK, 0); //将传递过来的事件从红黑树中删除
    timer_stats_account_hrtimer(timer);
    fn = timer->function;                                     //获取到期时钟的处理函数

    /*
     * Because we run timers from hardirq context, there is no chance
     * they get migrated to another cpu, therefore its safe to unlock
     * the timer base.
     */
    raw_spin_unlock(&cpu_base->lock);
    trace_hrtimer_expire_entry(timer, now);
    restart = fn(timer);                                      //处理timer
    trace_hrtimer_expire_exit(timer);
    raw_spin_lock(&cpu_base->lock);

    /*
     * Note: We clear the CALLBACK bit after enqueue_hrtimer and
     * we do not reprogramm the event hardware. Happens either in
     * hrtimer_start_range_ns() or in hrtimer_interrupt()
     */
    if (restart != HRTIMER_NORESTART) {
        BUG_ON(timer->state != HRTIMER_STATE_CALLBACK);
        enqueue_hrtimer(timer, base);
    }

    WARN_ON_ONCE(!(timer->state & HRTIMER_STATE_CALLBACK));

    timer->state &= ~HRTIMER_STATE_CALLBACK;
}



 



最后

以上就是怡然鼠标为你收集整理的High-Resolution Timers的全部内容,希望文章能够帮你解决High-Resolution Timers所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(46)

评论列表共有 0 条评论

立即
投稿
返回
顶部