1. Linux下有两类时钟:
1.1 实时钟RTC
它由板上电池驱动的“Real Time Clock”也叫做RTC或者叫CMOS时钟,硬件时钟。当操作系统关机的时候,用这个来记录时间,但是对于运行的系统是不用这个时间的。
1.2 系统时钟
“System clock”也叫内核时钟或者软件时钟,是由软件根据时间中断来进行计数的,内核时钟在系统关机的情况下是不存在的,所以,当操作系统启动的时候,内核时钟是要读取RTC时间来进行时间同步.
2. [url=]标准计时器[/url]
2.1 时钟滴答计时(jiffies)的几个基本参数
2.1.1 时钟周期(clock cycle)的频率-晶振频率
计时器Timer晶体振荡器在1秒内产生的时钟脉冲个数就是时钟周期的频率, 要注意把这个Timer的时钟周期频率与时钟中断的频率区别开来, Linux用宏CLOCK_TICK_RATE来表示计时器的输入时钟脉冲的频率(比如我的为#define CLOCK_TICK_RATE 1193180),该宏定义在arm/mach-xxx/include/mach/timex.h头文件中。
2.1.2 时钟中断(clock tick)
我们知道当计数器减到0值时,它就在IRQ0上产生一次时钟中断,也即一次时钟中断, 计数器的初始值决定了要过多少时钟周期才产生一次时钟中断,因此也就决定了一次时钟滴答的时间间隔长度.
2.1.3 时钟中断的频率(HZ)
即1秒时间内Timer所产生的时钟中断次数。确定了时钟中断的频率值后也就可以确定Timer的计数器初值。Linux内核用宏HZ来表示时钟中断的频率,而且在不同的平台上HZ有不同的定义值。对于SPARC、MIPS、ARM和i386等平台HZ的值都是100。该宏在ARM平台上的定义如下(/arch/arm/include/asm/param.h)
2.1.4 计数器的初始值
计数器的初始值由宏LATCH定义在文件:include/linux/jiffies.h
#define LATCH ((CLOCK_TICK_RATE + HZ/2) / HZ) /* For divider */
2.1.5 jiffies
在 Linux 内核中,时间由一个名为 jiffies 的全局变量衡量,该变量标识系统启动以来经过的滴答数。在最低的级别上,计算滴答数的方式取决于正在运行的特定硬件平台;但是,滴答计数通常在一次中断期间仍然继续进行。
标准计时器 API 作为 Linux 内核的一部分已经有很长一段时间了(自从 Linux 内核的早期版本开始)。尽管它提供的精确性比高精确度计时器要低,但它对于在处理物理设备时提供错误覆盖的传统驱动程序超时来说比较理想。在很多情况下,这些超时实际上从不触发,而是被启动,然后被删除。
简单内核计时器使用计时器轮(timer wheel) 实现。这个主意是由 Finn Arne Gangstad 在 1997 年首次引入的。它不理睬管理大量计时器的问题,而是很好地管理数量合理的计时器 — 典型情况。(原始计时器实现只是按照过期顺序将计时器实现双重链接。尽管在概念上比较简单,但这种方法是不可伸缩的。)时间轮是一个 buckets 集合,其中每个 bucker 表示将来计时器过期的一个时间块。这些 buckets 使用基于 5 个 bucket 的对数时间定义。使用 jiffies 作为时间粒度,定义了几个组,它们表示将来的过期时段(其中每个组通过一列计时器表示)。计时器插入使用具有 O(1) 复杂度的列表操作发生,过期发生在 O(N) 时间内。计时器过期以串联的形式出现,其中计时器被从高粒度 buckets 删除,然后随着它们的过期时间的下降被插入到低粒度 buckets 中。现在我们查看一下针对这个计时器实现的 API。
2.2 计时器 API
Linux 提供了一个简单的 API 来构造和管理计时器。它包含一些函数(和助手函数),用于创建、取消和管理计时器。
计时器通过 timer_list 结构定义,该结构包括实现一个计时器所需的所有数据(其中包括列表指针和在编译时配置的可选计时器统计数据)。从用户角度看,timer_list 包含一个过期时间,一个回调函数(当/如果计时器过期),以及一个用户提供的上下文。用户必须初始化计时器,可以采取几种方法,最简单的方法是调用 setup_timer,该函数初始化计时器并设置用户提供的回调函数和上下文。或者,用户可以设置计
- void init_timer( struct timer_list *timer );
- void setup_timer( struct timer_list *timer,
- void (*function)(unsigned long), unsigned long data );
、
2.3 计时器示例
我们来检查一下这些 API 函数的实际运行情况。下面的代码提供了一个简单的内核模块,用于展示简单计时器 API 的核心特点。在 init_module 中,您使用 setup_timer 初始化了一个计时器,然后调用 mod_timer 来启动它。当计时器过期时,将调用回调函数 my_timer_callback。
- struct hrtimer {
- struct rb_node node;
- ktime_t _expires;
- ktime_t _softexpires;
- enum hrtimer_restart (*function)(struct hrtimer *);
- struct hrtimer_clock_base *base;
- unsigned long state;
- #ifdef CONFIG_TIMER_STATS
- int start_pid;
- void *start_site;
- char start_comm[16];
- #endif
- };
- void hrtimer_init( struct hrtimer *time, clockid_t which_clock,
- enum hrtimer_mode mode );
- int hrtimer_start(struct hrtimer *timer, ktime_t time, const
- enum hrtimer_mode mode);
hrtimer 启动后,可以通过调用 hrtimer_cancel 或 hrtimer_try_to_cancel 来取消。每个函数都包含将被停止的计时器的 hrtimer 引用。这两个函数的区别在于:hrtimer_cancel 函数试图取消计时器,但如果计时器已经发出,那么它将等待回调函数结束;hrtimer_try_to_cancel 函数也试图取消计时器,但如果计时器已经发出,它将返回失败。
- int hrtimer_cancel(struct hrtimer *timer);
- int hrtimer_try_to_cancel(struct hrtimer *timer);
可以通过调用 hrtimer_callback_running 来检查 hrtimer 是否已经激活它的回调函数。注意,这个函数由 hrtimer_try_to_cancel 内部调用,以便在计时器的回调函数被调用时返回一个错误。
[cpp] view plain copy
- int hrtimer_callback_running(struct hrtimer *timer);
3.2 一个 hrtimer 示例
- #include <linux/kernel.h>
- #include <linux/module.h>
- #include <linux/hrtimer.h>
- #include <linux/ktime.h>
- MODULE_LICENSE("GPL");
- #define MS_TO_NS(x) (x * 1E6L)
- static
struct hrtimer hr_timer;
- enum hrtimer_restart my_hrtimer_callback( struct hrtimer *timer )
- {
- printk( "my_hrtimer_callback called (%ld).\n", jiffies );
- return HRTIMER_NORESTART;
- }
- int init_module( void )
- {
- ktime_t ktime;
- unsigned long delay_in_ms = 200L;
- printk("HR Timer module installing\n");
- ktime = ktime_set( 0, MS_TO_NS(delay_in_ms) );
- hrtimer_init( &hr_timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL );
- hr_timer.function = &my_hrtimer_callback;
- printk( "Starting timer to fire in %ldms (%ld)\n", delay_in_ms, jiffies );
- hrtimer_start( &hr_timer, ktime, HRTIMER_MODE_REL );
- return 0;
- }
- void cleanup_module( void )
- {
- int ret;
- ret = hrtimer_cancel( &hr_timer );
- if (ret) printk("The timer was still in use...\n");
- printk("HR Timer module uninstalling\n");
- return;
- }
关于 hrtimer API,还有许多内容这里没有涉及到。一个有趣的方面是它能够定义回调函数的执行上下文(比如在 softirq 或 hardiirq 上下文中)。您可以在 ./include/linux/hrtimer.h 文件中进一步了解 hrtimer API。 |