系列文章目录
Linux 内核设计与实现
深入理解 Linux 内核
深入理解 Linux 内核(二)
Linux 设备驱动程序
Linux设备驱动开发详解
文章目录
- 系列文章目录
- 五、定时测量
- 1、时钟和定时器电路
- 2、Linux 计时体系结构
- (1)计时体系机构的数据结构
- (2)软定时器和延迟函数
- (a)动态定时器
五、定时测量
1、时钟和定时器电路
- 实时时钟(RTC)
- 时间戳计数器(TSC)
- 可编程间隔定时器(PIT)
- CPU 本地定时器
- 高精度时间定时器(HPET)
- ACPI 电源管理定时器
2、Linux 计时体系结构
(1)计时体系机构的数据结构
- 定时器对象
- jiffies 变量
- xtime 变量
(2)软定时器和延迟函数
Linux定时器分为动态定时器(dynamic timer)和间隔定时器(interval timer)。第一种类型由内核使用,而间隔定时器可以由进程在用户态创建。
这里是有关 Linux 定时器的警告:因为对定时器函数的检查总是由可延迟函数进行,而可延迟函数被激活以后很长时间才能被执行,因此,内核不能确保定时器面数正好在定时到期时开始执行,而只能保证在适当的时间执行它们,或者假定延迟到几百毫秒之后执行它们。因此,对于必须严格遵守定时时间的那些实时应用而言,定时器并不适合。
除了软定时器外,内核还使用了延迟函数,它执行一个紧凑的指令循环直到指定的时间间隔用完。我们将在后面的 “延迟函数” 一节对它们进行讨论。
(a)动态定时器
动态定时器(dynamic timer)被动态地创建和撤消,对当前活动动态定时器的个数没有限制。
// include/linux/timer.h
struct timer_list {
struct list_head entry;
unsigned long expires;
void (*function)(unsigned long);
unsigned long data;
struct tvec_base *base;
#ifdef CONFIG_TIMER_STATS
void *start_site;
char start_comm[16];
int start_pid;
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
function 字段包含定时器到期时执行函数的地址。data 字段指定传递给定时器函数的参数。正是由于 data 字段,就可以定义一个单独的通用函数来处理多个设备驱动程序的超时问题,在 data 字段可以存放设备 ID,或其他有意义的数据,定时器函数可以用这些数据区分不同的设备。
expires 字段给出定时器到期时间,时间用节拍数表示,其值为系统启动以来所经过的节拍数。当 expires 的值小于或等于 jiffies 的值时,就说明计时器到期或终止。
entry 字段用于将软定时器插入双向循环链表队列中,该链表根据定时器 expires 字段的值将它们分组存放。我们将在本章后面描述使用这些链表的算法。
为了创建并激活一个动态定时器,内核必须:
- 如果需要,创建一个新的 timer_list 对象,比如说设为 t。这可以通过以下几种方式来进行:
- 在代码中定义一个静态全局变量。
- 在函数内定义一个局部变量:在这种情况下,这个对象存放在内核堆栈中。
- 在动态分配的描述符中包含这个对象。
- 调用 init_timer(&t) 函数初始化这个对象。实际上是把 t.base 指针字段置为 NULL 并把 t.lock 自旋锁设为 “打开”。
- 把定时器到期时激活函数的地址存入 function 字段。如果需要,把传递给函数的参数值存入 data 字段。
- 如果动态定时器还没有被插入到链表中,给 expires 字段赋一个合适的值并调用 add_timer(&t) 函数把 t 元素插入到合适的链表中
- 否则,如果动态定时器已经被插入到链表中,则调用 mod_timer() 函数来更新 expires 字段,这样也能将对象插入到合适的链表中(下面将讨论)。