jiffies

来源:互联网 发布:java选择排序代码 编辑:程序博客网 时间:2024/05/16 01:34

HZ

Linux内核每隔固定周期都会发生时钟中断, 而HZ代表系统在1s中发生时钟中断的次数。如果HZ=1000,则系统在1s之内会发生1000次时钟中断。
HZ的值可以在kernel的配置文件config中配置,其中可以配置为100, 250, 1000等。

# CONFIG_HZ_100 is not setCONFIG_HZ_250=y# CONFIG_HZ_300 is not set# CONFIG_HZ_1000 is not setCONFIG_HZ=250
可以看到自己的系统中配置的是250, 也就是说1s之内会发生250次时钟中断。

jiffies

Linux内核使用全局变量jiffies记录系统自从启动以来的滴答数。在系统启动的时初始化jiffies为0,在每个时钟中断处理例程中该值会加1。假如HZ=1000,每隔1ms系统会发生一次时钟中断,也就是说每隔1ms jiffies的值会加1,也就是说1s内jiffies的值也是HZ,所以系统启动的时间就是: jiffies/HZ

在Linux中jiffies的声明如下:
#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"中.
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

#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的值即可。
#if (BITS_PER_LONG < 64)u64 get_jiffies_64(void);#elsestatic inline u64 get_jiffies_64(void){return (u64)jiffies;}#endif

时间比较

系统中有时候需要对两个时间做比较,来确认时间点的前后。因为jiffies在每次时钟中断的时候都发生变化,所以就可以通过比较两个时间点的jiffies来比较。如果jiffies如果没有溢出,那就非常容易比较,不就是一大一小数值比较。但是溢出的可能性是存在的,所以就需要考虑到。所以linux内核为时间比较提供了一些列API。
#define time_after(a,b)\(typecheck(unsigned long, a) && \ typecheck(unsigned long, b) && \ ((long)((b) - (a)) < 0))
如果时间a在时间b之后,则返回true

#define time_before(a,b)time_after(b,a)
如果时间a在时间b前面,则返回true
#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。
/* * 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内核为此提供了一组相关函数:
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时间转化为秒,微秒,纳秒
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
int gettimeofday(struct timeval *tv, struct timezone *tz);int settimeofday(const struct timeval *tv, const struct timezone *tz)
其实就用到了struct timeval结构体
struct timeval {__kernel_time_ttv_sec;/* seconds */__kernel_suseconds_ttv_usec;/* microseconds */};
timeval是由秒和微秒组成,__kernel_time_t和__kernel_suseconds_t都是long型变量。

再比如系统调用,clock_gettime/clock_settime
int clock_gettime(clockid_t clk_id, struct timespec *tp);int clock_settime(clockid_t clk_id, const struct timespec *tp);
其中就用到了struct timespec结构体
struct timespec {__kernel_time_ttv_sec;/* seconds */longtv_nsec;/* nanoseconds */};
timespec是有秒和纳秒组成。

同样内核也提供了jiffies和这两个结构体之间的转化。
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,



0 0