Linux内核时间管理子系统——timekeeping

来源:互联网 发布:有关网络暴力的法律 编辑:程序博客网 时间:2024/06/05 23:59

时间的表示

在内核当中有几个不同的结构用于表示时间。

timespec

该数据结构来自于POSIX.1b规范,用于在用户态和内核态之间传递时间信息。POSIX还定义了许多API供用户态调用,例如clock_gettime(),clock_settime()等,其中的表示时间的结构都是timespec。在内核中该结构定义在include/uapi/linux/time.h。
struct timespec {        __kernel_time_t tv_sec;                 /* seconds */        long            tv_nsec;                /* nanoseconds */};

timeval

该结构也用于用户态和内核态间的时间信息传递。不同于timespec的是它的两个成员分别是秒和微妙,精度要比timespec差。另外一个区别是相关API也不同,timeval相关的API是gettimeofday()等。
struct timeval {        __kernel_time_t         tv_sec;         /* seconds */        __kernel_suseconds_t    tv_usec;        /* microseconds */};

ktime

不同于timespec,ktime定义于include/linux/ktime.h,且仅用于内核当中。对于64位系统而言,该结构就是一个64位有符号数。
union ktime {        s64     tv64;#if BITS_PER_LONG != 64 && !defined(CONFIG_KTIME_SCALAR)        struct {# ifdef __BIG_ENDIAN        s32     sec, nsec;# else        s32     nsec, sec;# endif        } tv;#endif};
内核提供了多种helper API用于不同时间结构之间的转化。
static inline ktime_t timespec_to_ktime(struct timespec ts)#define ktime_to_timespec(kt)           ns_to_timespec((kt).tv64)static inline ktime_t timeval_to_ktime(struct timeval tv)#define ktime_to_timeval(kt)            ns_to_timeval((kt).tv64)
内核提供了多种API用于将ktime转化为更常见的毫秒、微妙、纳秒等时间单位。
static inline s64 ktime_to_us(const ktime_t kt)static inline s64 ktime_to_ms(const ktime_t kt)#define ktime_to_ns(kt)                 ((kt).tv64)
另外内核还提供了用于对ktime之间进行加减等运算,对ktime和us、ns、ms之间进行加减等运算的API,详见include/linux/ktime.h。

时间的类型

下面这些时间类型均来自POSIX,详见clock_gettime()的manpage。

CLOCK_REALTIME

即wall clock(墙上时间),真实世界的时间(某年某月某日)。该时间可以被人为跳跃的修改,如date命令,或渐进的修改,如使用adjtime()函数或NTP。

CLOCK_MONOTONIC

是一种单调增长的时间。改时间不能被人为跳跃修改,但是可以被渐进的修改。

CLOCK_BOOTTIME

系统的运行时间。类似于CLOCK_MONOTONIC,区别是包含睡眠时间。

CLOCK_MONOTONIC_RAW

与CLOCK_MONOTONIC类似,但是是一种完全基于硬件的时间,甚至不能被渐进的修改(adjtime()或NTP)。

时间的记录

以上提到的各种时间均被内核使用timekeeper结构体记录。该结构定义于include/linux/timekeeper_internal.h。
/* Structure holding internal timekeeping values. */struct timekeeper {        /* Current clocksource used for timekeeping. */        struct clocksource      *clock;        /* NTP adjusted clock multiplier */        u32                     mult;        /* The shift value of the current clocksource. */        u32                     shift;        /* Number of clock cycles in one NTP interval. */        cycle_t                 cycle_interval;        /* Last cycle value (also stored in clock->cycle_last) */        cycle_t                 cycle_last;        /* Number of clock shifted nano seconds in one NTP interval. */        u64                     xtime_interval;        /* shifted nano seconds left over when rounding cycle_interval */        s64                     xtime_remainder;        /* Raw nano seconds accumulated per NTP interval. */        u32                     raw_interval;        /* Current CLOCK_REALTIME time in seconds */        u64                     xtime_sec;        /* Clock shifted nano seconds */        u64                     xtime_nsec;        /* Difference between accumulated time and NTP time in ntp         * shifted nano seconds. */        s64                     ntp_error;        /* Shift conversion between clock shifted nano seconds and         * ntp shifted nano seconds. */        u32                     ntp_error_shift;        /*         * wall_to_monotonic is what we need to add to xtime (or xtime corrected         * for sub jiffie times) to get to monotonic time.  Monotonic is pegged         * at zero at system boot time, so wall_to_monotonic will be negative,         * however, we will ALWAYS keep the tv_nsec part positive so we can use         * the usual normalization.         *         * wall_to_monotonic is moved after resume from suspend for the         * monotonic time not to jump. We need to add total_sleep_time to         * wall_to_monotonic to get the real boot based time offset.         *         * - wall_to_monotonic is no longer the boot time, getboottime must be         * used instead.         */        struct timespec         wall_to_monotonic;        /* Offset clock monotonic -> clock realtime */        ktime_t                 offs_real;        /* time spent in suspend */        struct timespec         total_sleep_time;        /* Offset clock monotonic -> clock boottime */        ktime_t                 offs_boot;        /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */        struct timespec         raw_time;        /* The current UTC to TAI offset in seconds */        s32                     tai_offset;        /* Offset clock monotonic -> clock tai */        ktime_t                 offs_tai;};

clocksource

指向当前使用的时钟源。

xtime_sec, xtime_nsec

墙上时间(wall clock)。xtime_sec代表墙上时间的秒部分,xtime_nsec是墙上时间的纳秒部分左移timekeeper->shift位(注意是左移后的值)。

wall_to_monotonic, offs_real

墙上时间加上wall_to_monotonic就得到了monotonic时间。相反地monotonic时间加上offs_real就得到了墙上时间。

raw_time

代表monotonic raw时间。

total_sleep_time, offs_boot

系统在休眠状态的总时间。将monotonic时间加上total_sleep_time就得到了boottime。相反地boottime加上offs_boot就得到了墙上时间。

获取时间的API

获取墙上时间
ktime_t ktime_get_real(void)void getnstimeofday(struct timespec *ts)
获取monotonic时间
ktime_t ktime_get(void)void ktime_get_ts(struct timespec *ts)
获取monotonic raw时间
void getrawmonotonic(struct timespec *ts)
获取boottime时间
void getboottime(struct timespec *ts)

jiffie

jiffie可以看作是对于时钟中断数目的一种计数。有两种jiffie变量,分别是jiffies和jiffies_64,声明在include/linux/jiffies.h。
extern u64 __jiffy_data jiffies_64;extern unsigned long volatile __jiffy_data jiffies;
在32位体系结构中,内核通过loader脚本的技巧,将jiffies实现为jiffies_64的低32位。对于64位系统这两个变量其实完全等价。
在32位系统中,jiffies_64由于无法进行原子操作,因此对他的读取需要通过下面的API完成。
static inline u64 get_jiffies_64(void)
jiffie每经过一个时钟中断会被加一。时钟中断的频率由宏定义HZ决定。而HZ则由配置内核使得选项CONFIG_HZ决定。一般是几百到一千,即几百赫兹到一千赫兹。

时间的更新

对于jiffie和系统当中各种类型时间的更新在函数do_timer()中完成。参数ticks代表经过了多少个时钟中断。在函数实现中首先更新jiffie,再调用update_wall_time()更新系统时间。
/* * Must hold jiffies_lock */void do_timer(unsigned long ticks){        jiffies_64 += ticks;        update_wall_time();        calc_global_load(ticks);}

update_wall_time

时间更新的最小粒度是一个NTP interval,即一个时钟中断间隔内的纳秒数。只有当时间流逝超过一个NTP interval时,timekeeper内的时间变量才会被更新。
#define NTP_INTERVAL_FREQ  (HZ)#define NTP_INTERVAL_LENGTH (NSEC_PER_SEC/NTP_INTERVAL_FREQ)
下面是timekeeper中与时间更新相关的几个域的定义。cycle_interval代表一个NTP interval中的时钟源周期数。由于可能无法整除,因此它是一个取整后的结果。xtime_interval代表cycle_interval个周期对应的纳秒数的左移shift位。将xtime_interval右移shift位就可以得到cycle_interval个周期的纳秒数。将xtime_remainder右移shift位可以得到cycle_interval个周期的纳秒数与NTP interval的差。这三个量的关系如下:
xtime_interval >> shift + xtime_remainder >> shift == NTP_INTERVAL

cycle_interval = [ NTP_INTERVAL * 2 ^ clock->shift  / clock->mult ]
xtime_interval = interval * clock->mult = [ NTP_INTERVAL / 2 ^ clock->shift  / clock->mult ] * clock->mult
xtime_remainder = NTP_INTERVAL * 2 ^ clock->shift - xtime_interval

每经过N个cycle_interval周期,内核将对xtime增加N * (xtime_interval >> shift)个纳秒数,完成时间更新。由于在计算过程中考虑到了对NTP的处理,真实的处理过程要更加复杂。











0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 四个月宝宝睡不踏实怎么办 5个月宝宝瘦了怎么办 宝宝只吃迷糊奶怎么办 宝宝五个月了不吃奶粉怎么办 1岁婴儿入睡困难怎么办 怀孕五个月胎儿肾积水怎么办 15个月宝宝总喊怎么办 学业水平考试有d怎么办 买了水果碰见领导怎么办 高一孩子不愿意上学怎么办 专家解答 孩子不愿意上学怎么办 冬天脚冷怎么办膝盖疼 拉拉裤大了怎么办小妙招 孕37周翻身困难怎么办 晚上睡不好白天犯困怎么办 抑郁症又不想起床了怎么办 儿童憋尿功能太差怎么办 一个月的宝宝睡眠不好怎么办 被商场要求撤场怎么办 上班没法接孩子放学怎么办 幼儿下午放学与上班错开怎么办 宝宝早上醒的早怎么办 晚卜睡不着尿多怎么办 要求正常休息公司不准怎么办 我早起被室友说怎么办? 小孩晚上睡的晚怎么办 初中一年级学不扎实怎么办 一年级学生上课爱说话怎么办 孩子不按时完成作业怎么办 高三理科基础不好怎么办 字写快了就难看怎么办 高一文科280分怎么办 高三了学不进去怎么办 副职兼任法人不够条件怎么办 正职和上级不和副职怎么办 中层正职和上级不和副职怎么办 陆军军官年龄大了怎么办 ps选区选多了怎么办 香港货物被海关扣了怎么办 羽绒服棉填充物不均匀了怎么办 蛀牙到牙神经了怎么办