Linux Kernel Development

Linux Kernel Development——定时器和时间管理Posted on

几个重要的名词HZ:系统定时器频率HZ用来定义系统定时器每隔1秒产生多少个时钟中断Tick:HZ的倒数,系统定时器两次时钟中断的时间间隔Xtime:记录Wall time值,也就是UTC时间,是一个struct timeval结构,在用户空间通过gettimeofday读取Jiffies:记录系统开机以来经过了多少次Tick,定义为unsigned long volatile __jiffy_data jiffies;RTC:实时时钟,是一个硬件时钟,用来持久存放系统时间HZ

HZ是静态编译到内核中的,其定义如下:

// /usr/include/asm-generic/param.h文件中

#ifdef __KERNEL__

# define HZCONFIG_HZ /* Internal kernel timer frequency */

# define USER_HZ 100/* some user interfaces are */

# define CLOCKS_PER_SEC (USER_HZ)/* in “ticks” like times() */

#endif

#ifndef HZ

#define HZ 100

#endif

在2.6以前的内核中,如果改变内核中的HZ值会给用户空间中某些程序造成异常结果。因为内核是以节拍数/秒的形式给用户空间导出这个值的,应用程序便依赖这个特定的HZ值。如果在内核中改变了HZ的定义值,就打破了用户空间的常量关系—用户空间并不知道新的HZ值。

内核更改所有导出的jiffies值。内核定义了USER_HZ来代表用户空间看到的HZ值。在x86体系结构上,由于HZ值原来一直是100,所以USER_HZ值就定义为100。内核可以使用宏jiffies_to_clock_t()将一个有HZ表示的节拍计数转换为一个由USER_HZ表示的节拍计数:

start=jiffies;

//doing some jobs

total_time=jiffies-start;

ticks=jiffies_to_clock_t(total_time);

jiffies_to_clock_t()函数的定义如下:

/*

* Convert jiffies/jiffies_64 to clock_t and back.

*/

clock_t jiffies_to_clock_t(unsigned long x)

{

#if (TICK_NSEC % (NSEC_PER_SEC / USER_HZ)) == 0

# if HZ < USER_HZ

return x * (USER_HZ / HZ);

# else

return x / (HZ / USER_HZ);

# endif

#else

return div_u64((u64)x * TICK_NSEC, NSEC_PER_SEC / USER_HZ);

#endif

}

Jiffies

Jiffies记录了系统启动以来所经过的ticks数,在开机时该值是0,随后每次时钟中断发生时加1.

根据jiffies可以计算系统的开机时间:jiffies/HZ 秒

Jiffies的定义为unsigned long volatile __jiffy_data jiffies;

由于jiffies存在溢出的可能,内核定义了一组辅助函数来处理jiffies的比较操作,香港服务器,操作jiffies时最好使用这些辅助函数:

/*

* These inlines deal with timer wrapping correctly. You are

* strongly encouraged to use them

* 1. Because people otherwise forget

* 2. Because if the timer wrap changes in future you won’t have to

*alter your driver code.

*

* time_after(a,b) returns true if the time a is after time b.

*

* Do this with “<0” and “>=0” to only test the sign of the result. A

* good compiler would generate better code (and a really good compiler

* wouldn’t care). Gcc is currently neither.

*/

#define time_after(a,b)\

(typecheck(unsigned long, a) && \

typecheck(unsigned long, b) && \

((long)(b) – (long)(a) < 0))

#define time_before(a,b) time_after(b,a)

#define time_after_eq(a,b) \

(typecheck(unsigned long, a) && \

typecheck(unsigned long, b) && \

((long)(a) – (long)(b) >= 0))

#define time_before_eq(a,b) time_after_eq(b,a)

实时时钟RTC

实时时钟是一个硬件时钟,用来持久存放系统时间,系统关闭后靠主板上的微型电池保持计时;

系统启动时,内核通过读取RTC来初始化Wall Time, 并存放在xtime变量中,这是RTC最主要的作用;

当用户修改了时间后,可以用hwclock –w将其保存到RTC中。

系统定时器

每个PC机中都有一个PIT,以通过IRQ0产生周期性的时钟中断信号,作为系统定时器 system timer。当发生时钟中断时,就会自动调用时钟中断处理程序。

时钟中断处理程序分为两个部分:体系结构相关部分和体系结构无关部分。相关的部分作为系统定时器的中断处理程序而注册到内核中,以便在产生时钟中断时,它能够相应地运行。执行的工作如下:

1.获得xtime_lock锁,以便对访问jiffies_64和墙上时间xtime进行保护。

2.需要时应答或重新设置系统时钟。

3.周期性地使用墙上时间更新实时时钟。

4.调用体系结构无关的时间例程:do_timer().

中断服务程序主要通过调用与体系结构无关的例程do_timer()执行下面的工作:

1.给jiffies_64变量加1.

2.更新资源消耗的统计值,比如当前进程所消耗的系统时间和用户时间。

3.执行已经到期的动态定时器.

4.执行scheduler_tick()函数.

5.更新墙上时间,该时间存放在xtime变量中.

6.计算平均负载值.

Xtime

记录Wall time值,也就是UTC时间,是一个struct timeval结构。

在内核空间读写这个xtime变量需要xtime_lock锁,该锁是一个顺序锁(seqlock)。

而在用户空间通过gettimeofday读取xtime,它在内核中对应系统调用为sys_gettimeofday()。

动态定时器

动态定时器并不周期执行,它在超时后就自行销毁。定义器由定义在linux/timer.h中的time_list表示,如下:

struct timer_list {

/*

* All fields that change during normal runtime grouped to the

* same cacheline

*/

struct list_head entry;

unsigned long expires;

struct tvec_base *base;

void (*function)(unsigned long);

unsigned long data;

int slack;

}

内核提供了一组函数用来简化管理定时器的操作。所有这些接口都声明在文件linux/timer.h中,大多数接口在文件kernel/timer.c中获得实现。有了这些接口,我们要做的事情就很简单了:

1.创建定时器:struct timer_list my_timer;

2.初始化定时器:init_timer(&my_timer);

3.根据需要,设置定时器了:

my_timer.expires = jiffies + delay;

my_timer.data = 0;

my_timer.function = my_function;

4.激活定时器:add_timer(&my_timer);

经过上面的几步,定时器就可以开始工作了。然而,一般来说,定时器都在超时后马上就会执行,但是也有可能被推迟到下一时钟节拍时才能运行,所以不能使用它来实现硬实时。

最重要的是今天的心。

Linux Kernel Development

相关文章:

你感兴趣的文章:

标签云: