HZ
Linux内核每隔固定周期都会发生时钟中断, 而HZ代表系统在1s中发生时钟中断的次数。如果HZ=1000,则系统在1s之内会发生1000次时钟中断。
HZ的值可以在kernel的配置文件config中配置,其中可以配置为100, 250, 1000等。
代码语言:javascript复制# CONFIG_HZ_100 is not set
CONFIG_HZ_250=y
# CONFIG_HZ_300 is not set
# CONFIG_HZ_1000 is not set
CONFIG_HZ=250
可以看到自己的系统中配置的是250, 也就是说1s之内会发生250次时钟中断。
jiffies
Linux内核使用全局变量jiffies记录系统自从启动以来的滴答数。在系统启动的时初始化jiffies为0,在每个时钟中断处理例程中该值会加1。假如HZ=1000,每隔1ms系统会发生一次时钟中断,也就是说每隔1ms jiffies的值会加1,也就是说1s内jiffies的值也是HZ,所以系统启动的时间就是: jiffies/HZ
在Linux中jiffies的声明如下:
代码语言:javascript复制#define __jiffy_data __attribute__((section(".data")))
/*
* The 64-bit value is not atomic - you MUST NOT read it
* without sampling the sequence number in jiffies_lock.
* get_jiffies_64() will do this for you as appropriate.
*/
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
根据定义可知,jiffies变量是定义在链接脚本"vmlinux.lds"中.
代码语言:javascript复制OUTPUT_ARCH(aarch64)
ENTRY(_text)
jiffies = jiffies_64;
可知jiffies_64和jiffies变量的地址是一样的,只是一个表示32位,一个表示64位。
相对于jiffies而言,jiffies是个64位的变量。在32位平台上,jiffies和jiffies_64的低32位是重合的,访问jiffies_64只取低32位。但是在64位平台上jiffies和jiffies_64是同一个变量。
jiffies的访问
如果在32位平台上访问jiffies,可以直接访问。 但是想访问jiffies_64,就不能直接访问。因为在直接读取jiffies_64的低32位的时候,就有可能jiffies_64的值发生了改变,所以必须使用系统提供的函数: get_jiffies_64
代码语言:javascript复制#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void)
{
unsigned long seq;
u64 ret;
do {
seq = read_seqbegin(&jiffies_lock);
ret = jiffies_64;
} while (read_seqretry(&jiffies_lock, seq));
return ret;
}
EXPORT_SYMBOL(get_jiffies_64);
#endif
内核使用顺序锁来访问jiffies_64,关于顺序锁,可以看“顺序锁小节”
如果是64位平台,也是使用上述的函数,但是实现不一样,64位平台就可以直接读取jiffies的值即可。
代码语言:javascript复制#if (BITS_PER_LONG < 64)
u64 get_jiffies_64(void);
#else
static inline u64 get_jiffies_64(void)
{
return (u64)jiffies;
}
#endif
时间比较
系统中有时候需要对两个时间做比较,来确认时间点的前后。因为jiffies在每次时钟中断的时候都发生变化,所以就可以通过比较两个时间点的jiffies来比较。如果jiffies如果没有溢出,那就非常容易比较,不就是一大一小数值比较。但是溢出的可能性是存在的,所以就需要考虑到。所以linux内核为时间比较提供了一些列API。
代码语言:javascript复制#define time_after(a,b)
(typecheck(unsigned long, a) &&
typecheck(unsigned long, b) &&
((long)((b) - (a)) < 0))
如果时间a在时间b之后,则返回true
代码语言:javascript复制#define time_before(a,b) time_after(b,a)
如果时间a在时间b前面,则返回true
代码语言:javascript复制#define time_after_eq(a,b)
(typecheck(unsigned long, a) &&
typecheck(unsigned long, b) &&
((long)((a) - (b)) >= 0))
#define time_before_eq(a,b) time_after_eq(b,a)
如果时间a在时间b之后或者相等则返回true。 如果时间a在时间b之前或者相同则返回true。
代码语言:javascript复制/*
* Calculate whether a is in the range of [b, c].
*/
#define time_in_range(a,b,c)
(time_after_eq(a,b) &&
time_before_eq(a,c))
该宏用于检查时间a是否在时间b和时间c之间,同时当a等于b或者a等于c的时候也会返回true 如果是对jiffies_64类型做时间比较,和只需要在每个函数后面添加64即可,例如:time_after64
时间转换
有时候,内核中需要将用jiffies表达的时间转化为毫秒ms或者微秒us的形式,,Linux内核为此提供了一组相关函数:
代码语言:javascript复制unsigned int jiffies_to_msecs(const unsigned long j);
unsigned int jiffies_to_usecs(const unsigned long j);
inline u64 jiffies_to_nsecs(const unsigned long j);
上述函数分别是将jiffies时间转化为秒,微秒,纳秒
代码语言:javascript复制unsigned long msecs_to_jiffies(const unsigned int m);
unsigned long usecs_to_jiffies(const unsigned int u);
unsigned long nsecs_to_jiffies(u64 n);
上述的函数是将秒,微秒,纳秒转化为jiffies.
时间转换的另一种形式
通过用户程序需要和内核,或者驱动程序打交道,这时候应用程序使用的时间就是以秒和毫秒为单位。比如系统调用gettimeofday/settiemofday
代码语言:javascript复制int gettimeofday(struct timeval *tv, struct timezone *tz);
int settimeofday(const struct timeval *tv, const struct timezone *tz)
其实就用到了struct timeval结构体
代码语言:javascript复制struct timeval {
__kernel_time_t tv_sec; /* seconds */
__kernel_suseconds_t tv_usec; /* microseconds */
};
timeval是由秒和微秒组成,__kernel_time_t和__kernel_suseconds_t都是long型变量。
再比如系统调用,clock_gettime/clock_settime
代码语言:javascript复制int clock_gettime(clockid_t clk_id, struct timespec *tp);
int clock_settime(clockid_t clk_id, const struct timespec *tp);
其中就用到了struct timespec结构体
代码语言:javascript复制struct timespec {
__kernel_time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
timespec是有秒和纳秒组成。
同样内核也提供了jiffies和这两个结构体之间的转化。
代码语言:javascript复制unsigned long timespec_to_jiffies(const struct timespec *value);
void jiffies_to_timespec(const unsigned long jiffies,struct timespec *value);
unsigned long timeval_to_jiffies(const struct timeval *value);
void jiffies_to_timeval(const unsigned long jiffies,