[linux]linux内核时间管理基础

来源:互联网 发布:java文件下载 前台 编辑:程序博客网 时间:2024/06/07 05:13

一,linux时间管理基础
http://blog.csdn.net/droidphone/article/details/7975694
http://blog.csdn.net/smilingjames/article/details/6205540
linux所有时间基础都是以低层硬件为基础的,低层硬件有GPT和cpu local timer,比如GPT的时钟源为13M HZlinux低层时间的架构分为clock source,clock event device,clock source上层分为Xtimer和Hrtimer,Xtimer主要是指墙上时间(开机的时候从RTC寄存器读取墙上时间),Hrtimer主要是高精度的计时器,精度可以到ns级别,Clock event Device向上层提供jiffies以及时间轮的概念,比如进程切换的最小精度是10ms。

struct clocksource,定义了一个clock device的基本属性及行为, 这些clock device一般都有计数,定时, 产生中断能力, 比如GPT
struct clock_event_device  Clock event的主要作用是分发clock事件及设置下一次触发条件. 在没有clock event之前,时钟中断都是周期性地产生, 也就是熟知的jiffies和HZ


二:jiffies和HZ的概念
在ARM系统上HZ的大小一般为100,表示1s内有100个节拍,jiffies表示的是系统自从启动以来的节拍总数,jiffies一般为unsigned long类型,所以可能会溢出。比如:unsigned long jiffies; unsigned long timeout=jiffies+HZ/2;表示的是未来的0.5s
jiffies回绕的问题:

01.unsigned long jiffies;02.unsigned long timeout = jiffies + HZ/2;03.//......04.if (timeout > jiffies) {05.        //没有超时,很好06.}07.else {08.        //超时了,发生错误09.}
其中jiffies是个不断在增大的unsigned long,timeout可以看作比jiffies“大不了多少”的unsigned long。当jiffies变得比2^32-1还要大的时候会发生溢出,“回绕”(wrap around)到0附近。此时,判断语句为真,虽然实际上超时了,但是判断为没有超时。
Linux内核提供了一组宏解决这个问题。其中宏time_after(a, b)是考虑可能的溢出情况后判断时间a是否在时间b之后(即“b < a”)。
01.#define time_after(a, b) ((long)(b) - (long)(a) < 0)


三,Clock Source Struct

1.  struct clocksource {  6.      cycle_t (*read)(struct clocksource *cs);  7.      cycle_t cycle_last;  8.      cycle_t mask;  9.      u32 mult;  10.     u32 shift;  11.     u64 max_idle_ns;  12.     u32 maxadj;  13. #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA  14.     struct arch_clocksource_data archdata;  15. #endif    17.     const char *name;  18.     struct list_head list;  19.     int rating;  20.     int (*enable)(struct clocksource *cs);  21.     void (*disable)(struct clocksource *cs);  22.     unsigned long flags;  23.     void (*suspend)(struct clocksource *cs);  24.     void (*resume)(struct clocksource *cs);  25.   26.     /* private: */  27. #ifdef CONFIG_CLOCKSOURCE_WATCHDOG  28.     /* Watchdog related data, used by the framework */  29.     struct list_head wd_list;  30.     cycle_t cs_last;  31.     cycle_t wd_last;  32. #endif  33. } ____cacheline_aligned; 

1  rating:时钟源的精度
同一个设备下,可以有多个时钟源,每个时钟源的精度由驱动它的时钟频率决定,比如一个由10MHz时钟驱动的时钟源,他的精度就是100nS。clocksource结构中有一个rating字段,代表着该时钟源的精度范围,它的取值范围如下:
1--99: 不适合于用作实际的时钟源,只用于启动过程或用于测试;
100--199:基本可用,可用作真实的时钟源,但不推荐;
200--299:精度较好,可用作真实的时钟源;
300--399:很好,精确的时钟源;
400--499:理想的时钟源,如有可能就必须选择它作为时钟源;
2  read回调函数
时钟源本身不会产生中断,要获得时钟源的当前计数,只能通过主动调用它的read回调函数来获得当前的计数值,注意这里只能获得计数值,也就是所谓的cycle,要获得相应的时间,必须要借助clocksource的mult和shift字段进行转换计算。
1.3  mult和shift字段
因为从clocksource中读到的值是一个cycle计数值,要转换为时间,我们必须要知道驱动clocksource的时钟频率F,一个简单的计算就可以完成:
t = cycle/F;
可是clocksource并没有保存时钟的频率F,因为使用上面的公式进行计算,需要使用浮点运算,这在内核中是不允许的,因此,内核使用了另外一个变通的办法,根据时钟的频率和期望的精度,事先计算出两个辅助常数mult和shift,然后使用以下公式进行cycle和t的转换:
t = (cycle * mult) >> shift;只要我们保证:F = (1 << shift) / mult;内核内部使用64位进行该转换计算:
xtime 是人们日常所使用的墙上时间
monotonic time  该时间自系统开机后就一直单调地增加,它不像xtime可以因用户的调整时间而产生跳变,不过该时间不计算系统休眠的时间,也就是说,系统休眠时,monotoic时间不会递增。内核用timekeeper结构来组织与时间相关的数据,它的定义如下


四, Struct timekeeper

14struct timekeeper {15      /* Current clocksource used for timekeeping. */16      struct clocksource     *clock;17      /* NTP adjusted clock multiplier */18      u32                    mult;19      /* The shift value of the current clocksource. */20      u32                    shift;21      /* Number of clock cycles in one NTP interval. */22      cycle_t                cycle_interval;23      /* Last cycle value (also stored in clock->cycle_last) */24      cycle_t                cycle_last;25      /* Number of clock shifted nano seconds in one NTP interval. */26      u64                    xtime_interval;27      /* shifted nano seconds left over when rounding cycle_interval */28      s64                    xtime_remainder;29      /* Raw nano seconds accumulated per NTP interval. */30      u32                    raw_interval;3132      /* Current CLOCK_REALTIME time in seconds */33      u64                    xtime_sec;34      /* Clock shifted nano seconds */35      u64                    xtime_nsec;3637      /* Difference between accumulated time and NTP time in ntp38      * shifted nano seconds. */39      s64                    ntp_error;40      /* Shift conversion between clock shifted nano seconds and41      * ntp shifted nano seconds. */42      u32                    ntp_error_shift;4344      /*45      * wall_to_monotonic is what we need to add to xtime (or xtime corrected46      * for sub jiffie times) to get to monotonic time.  Monotonic is pegged47      * at zero at system boot time, so wall_to_monotonic will be negative,48      * however, we will ALWAYS keep the tv_nsec part positive so we can use49      * the usual normalization.50      *51      * wall_to_monotonic is moved after resume from suspend for the52      * monotonic time not to jump. We need to add total_sleep_time to53      * wall_to_monotonic to get the real boot based time offset.54      *55      * - wall_to_monotonic is no longer the boot time, getboottime must be56      * used instead.57      */58      struct timespec        wall_to_monotonic;59      /* Offset clock monotonic -> clock realtime */60      ktime_t                offs_real;61      /* time spent in suspend */62      struct timespec        total_sleep_time;63      /* Offset clock monotonic -> clock boottime */64      ktime_t                offs_boot;65      /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */66      struct timespec        raw_time;67      /* The current UTC to TAI offset in seconds */68      s32                    tai_offset;69      /* Offset clock monotonic -> clock tai */70      ktime_t                offs_tai;7172};

内核定义了一个变量wall_to_monotonic,记录了墙上时间和monotonic时间之间的偏移量,当需要获得monotonic时间时,把xtime和wall_to_monotonic相加即可,
Timekeeper的初始化,timekeeper的初始化由timekeeping_init完成,该函数在start_kernel的初始化序列中被调用,时间的更新:xtime一旦初始化完成后,timekeeper就开始独立于RTC,利用自身关联的clocksource进行时间的更新操作,根据内核的配置项的不同,更新时间的操作发生的频度也不尽相同,如果没有配置NO_HZ选项,通常每个tick的定时中断周期,do_timer会被调用一次。
 


0 0
原创粉丝点击