Chinaunix首页 | 论坛 | 博客
  • 博客访问: 62512
  • 博文数量: 28
  • 博客积分: 2000
  • 博客等级: 大尉
  • 技术积分: 325
  • 用 户 组: 普通用户
  • 注册时间: 2007-08-28 13:47
文章分类
文章存档

2011年(1)

2010年(27)

我的朋友

分类: LINUX

2010-07-21 16:58:25

LINUX内核定时器
2010-05-17 14:21
内核中许多部分的工作都高度依赖于时间信息。Linux内核利用硬件提供的定时器以支持忙等待或睡眠等待等时间相关的服务。忙等待时,CPU 不断运转。但是睡眠等待时,进程将放弃CPU。因此,只有在后者不可行的情况下,才考虑使用前者。内核也提供了某些便利,可以在特定的时间之后调度某函数运行。
我们首先来讨论一些重要的内核定时器变量(jiffies、HZ和xtime)的含义。
1 HZ和Jiffies
hz就是一秒钟产生的时间片的数量,也就是时钟发生器产生时钟中断的次数 。
系统定时器能以可编程的频率中断处理器。此频率即为每秒的定时器节拍数,对应着内核变量 HZ。选择合适的HZ值需要权衡。HZ值大,定时器间隔时间就小,因此进程调度的准确性会更高。但是,HZ值越大也会导致开销和电源消耗更多,因为更多的处理器周期将被耗费在定时器中断上下文中。
HZ的值取决于体系架构。在x86系统上,在2.4内核中,该值默认设置为100;在 2.6内核中,该值变为1000;而在2.6.13中,它又被降低到了250。在基于ARM的平台上,2.6 内核将HZ设置为100。在目前的内核中,可以在编译内核时通过配置菜单选择一个HZ值。该选项的默认值取决于体系架构的版本。
2.6.21内核支持无节拍的内核(CONFIG_NO_HZ),它会根据系统的负载动态触发定时器中断。无节拍系统的实现超出了本章的讨论范围,不 再详述。

jiffies变量记录了系统启动以来,系统定时器已经触发的次数。内核每秒钟将jiffies变量增加HZ次。因此,对于HZ值为100的系统,1个jiffy等于10ms,而对于HZ为1000的系统,1个jiffy仅为1ms。

为了更好地理解HZ和jiffies变量,请看下面的取自IDE驱动程序(drivers/ide/ide.c)的代码片段。该段代码会一直轮询磁盘驱 动器的忙状态:

unsigned long timeout = jiffies + (3*HZ);
while (hwgroup->busy) {
/* ... */
if (time_after(jiffies, timeout)) {
return -EBUSY;
}
/* ... */
}
return SUCCESS;

如果忙条件在3s内被清除,上述代码将返回SUCCESS,否则,返回-EBUSY。3*HZ是3s内的jiffies数量。计算出来的超时 jiffies + 3*HZ将是3s超时发生后新的jiffies值。time_after()的功能是将目前的jiffies值与请求的超时时间对比,检测溢出。类似函数 还包括time_before()、time_before_eq()和time_after_eq()。

jiffies被定义为 volatile类型,它会告诉编译器不要优化该变量的存取代码。这样就确保了每个节拍发生的定时器中断处理程序都能更新jiffies值,并且循环中的 每一步都会重新读取jiffies值。

假定jiffies值为1000,32位的jiffies会在 大约50天的时间内溢出。由于系统的运行时间可以比该时间长许多倍,因此,内核提供了另一个变量jiffies_64以存放64位(u64)的 jiffies。链接器将jiffies_64的低32位与32位的jiffies指向同一个地址。在32位的机器上,为了将一个u64变量赋值给另一 个,编译器需要2条指令,因此,读jiffies_64的操作不具备原子性。可以将drivers/cpufreq/cpufreq_stats.c文件 中定义的cpufreq_stats_update()作为实例来学习。

2 全局变量xtime

xtime是个timeval结构类型的变量,用来表示当前时间距UNIX时间基准1970-01-01 00:00:00的相对秒数值。
结构timeval是Linux内核表示时间的一种格式(Linux内核对时间的表示有多种格式,每种格式都有不同的时间精度),其时间精度是微秒。该结构是内核表示时间时最常用的一种格式,它定义在头文件include/linux/time.h中,如下所示:

struct timeval { 
time_t tv_sec; /* seconds */ 
suseconds_t tv_usec; /* microseconds */ 
}; 
其中,成员tv_sec表示当前时间距UNIX时间基准的秒数值,而成员tv_usec则表示一秒之内的微秒值,且1000000>tv_usec>=0。 
Linux内核通过timeval结构类型的全局变量xtime来维持当前时间,该变量定义在kernel/timer.c文件中,如下所示:
/* The current time */ 
volatile struct timeval xtime __attribute__ ((aligned (16))); 
不过,全局变量xtime所维持的当前时间通常是供用户来检索和设置的,而其他内核模块通常很少使用它(其他内核模块用得最多的是 jiffies),因此对xtime的更新并不是一项紧迫的任务,所以这一工作通常被延迟到时钟中断的底半部(bottom half)中来进行。由于bottom half的执行时间带有不确定性,因此为了记住内核上一次更新xtime是什么时候,Linux内核定义了一个类似于jiffies的全局变量 wall_jiffies,来保存内核上一次更新xtime时的jiffies值。时钟中断的底半部分每一次更新xtime的时侯都会将 wall_jiffies更新为当时的jiffies值。全局变量wall_jiffies定义在kernel/timer.c文件中: 
/* jiffies at the most recent update of wall time */ 
unsigned long wall_jiffies;

阅读(620) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~