linux时间子系统(一)

简单介绍linux下的时间子系统。包括clocksource,timekeeper和定时器的内容。

1、简介

时间子系统是操作系统不可或缺的一个重要组成部分。Linux的时间子系统的功能包含两部分,分别是保存当前时间和维持定时器。如下图所示,在Linux内核的通用时间框架中,使用timekeeper来维护当前时间,使用tick_device或者hrtimer来处理定时器的功能。之后我们将讨论一下timekeeper部分和定时器部分在内核中的实现和使用。


 

2、timekeeper

2.1 clocksource

在硬件层,时钟源通常是一个以固定时钟频率驱动的计数器,计数器只能单调增加,直到溢出为止。而在Linux内核中,使用clocksource结构完成对时钟源的封装。在clocksource结构中,记录了与时钟源有关的信息,包括频率,精度以及返回值为cycle_t类型的回调函数。通过回调函数,我们可以获取在某一时刻,时钟源硬件中的计数值。下面通过分析clocksource结构,了解clocksource的工作原理。

2.1.1 struct clocksource

struct clocksource {

        /*

         * Hotpath data, fits in a single cache line when the

         * clocksource itself is cacheline aligned.

         */

        cycle_t (*read)(struct clocksource *cs);

        cycle_t cycle_last;

        cycle_t mask;

        u32 mult;

        u32 shift;

        u64 max_idle_ns;

 

#ifdef CONFIG_IA64

        void *fsys_mmio;        /* used by fsyscall asm code */

#define CLKSRC_FSYS_MMIO_SET(mmio, addr)      ((mmio) = (addr))

#else

#define CLKSRC_FSYS_MMIO_SET(mmio, addr)      do { } while (0)

#endif

        const char *name;

        struct list_head list;

        int rating;

        cycle_t (*vread)(void);

        int (*enable)(struct clocksource *cs);

        void (*disable)(struct clocksource *cs);

        unsigned long flags;

        void (*suspend)(struct clocksource *cs);

        void (*resume)(struct clocksource *cs);

 

#ifdef CONFIG_CLOCKSOURCE_WATCHDOG

        /* Watchdog related data, used by the framework */

        struct list_head wd_list;

        cycle_t cs_last;

        cycle_t wd_last;

#endif

} ____cacheline_aligned;

   只需要关注clocksource的几个重要字段,就能初步了解clocksource的工作原理。

2.1.1.1 rating

rating,表示时钟源的精度。在同一台设备中,可以有多个时钟源,时钟源的精度与其时钟频率有关,频率越高,时钟源的rating值越大。rating值代表着该时钟源的精度范围,取值范围如下:

* 0-99: 不适合用作实际的时钟源,只用于启动过程或用于测试。

* 100-199: 基本可用,可用作真实的时钟源,但不推荐。

* 200-299: 精度较好,可用做真实的时钟源。

* 300-399: 很好,精确的时钟源。

* 400-499: 理想的时钟源,如有可能,必须选择它作为时钟源。*/

2.1.1.2 read回调函数

时钟源本身不会产生中断。要获取时钟源的计数值,只能通过主动调用其read回调函数,来获取当前时钟源的计数值。read回调函数的实现,就是读取时钟源的相关寄存器,获取其中的计数值。

2.1.1.3 mult和shift

由于使用read函数只能从时钟源硬件中获取一个cycle计数值,如果我们需要将其转换为时间,则需要知道当前时钟源的时钟频率F。这样t=cycle/F就可以将计数值转换为时间。在时钟源初始化时,可以从时钟源的寄存器中得到频率F的值,但是由于内核中不支持浮点运算,所以内核使用乘法和移位操作来取代除法操作。使用公式t=(cycle * mult) >> shift来替代t=cycle/F,把计数值转化为时间。

从转换精度考虑,mult的值越大越好。但是为了计算过程中cycle*mult不发生溢出,mult的值不能取值过大。为此内核假设cycle计数值被转换后的最大时间值为10分钟,主要的考虑是CPU进入IDLE状态后,时间信息不会被更新。只要在10分钟内退出IDLE状态,时钟源的cycle计数值就可以被正确的转换为相应的时间,然后系统时间可以被正确的更新。这个值不一定是10分钟,它由函数clocksource_max_deferment进行计算,并保存到max_idle_ns字段中。这里,我们使用10分钟这个假设值,推算出合适的mult和shift的值。

2.1.1.4 max_idle_ns

使用函数clocksource_max_deferment计算出合理的max_idle_ns的值。在内核中,tickless的代码要考虑这个值,以防止在NO_HZ配置环境下,系统保持IDLE状态时间过长,导致溢出。

2.1.1.5 cycle_last

  字段cycle_last记录了上一次调用read回调函数获取的cycle的大小。这样,我们就可以知道当前时刻和最近一次调用read回调函数的时间差。

 

 

(未完待续。。。)

猜你喜欢

转载自blog.csdn.net/kylinos_123/article/details/79569290