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
- Linux内核时间管理子系统——timekeeping
- Linux时间子系统之(四):timekeeping
- Linux时间子系统之(四):timekeeping
- Linux时间子系统之(四):timekeeping
- Linux内核时间管理子系统——时钟源
- linux 内核管理子系统
- Linux内核工程导论——电源管理子系统
- Linux内核子系统---内存管理子系统、进程管理子系统
- Linux 内核内存管理子系统
- linux 内核进程管理子系统
- Linux内核架构 Linux设备驱动 Linux电源管理 Linux音频子系统 Linux中断子系统 Linux时间管理系统 Linux输入子系统
- Linux内核(2)——子系统
- Linux内核时间管理
- Linux内核时间管理
- linux内核时间管理
- linux内核内存管理子系统概要分析
- Linux 内核子系统之内存与进程管理子系统
- Linux内核——定时器和时间管理
- Eclipse蛋疼的变量自动补全类名解决方法
- {转载}——时间复杂度和空间复杂度详解
- CameraApp编写启动篇
- poj 2886 Who Gets the Most Candies?*
- RabbitMQ学习(六).NET Client之RPC
- Linux内核时间管理子系统——timekeeping
- 【mDNS】本地DNS解析协议
- PyQuery: 一个类似jQuery的Python库
- HTML5 学习笔记 (2)
- HttpClient工具类
- C语言中memset函数详解
- POJ_1787_多重背包方案记录
- spring MVC
- UVA10763