jiffies

2020-03-24 16:34:56 浏览数 (1)

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,

0 人点赞