定时器和时间管理
内核中大量函数是基于时间驱动的,如定期执行的函数,定时执行的函数。系统定时器是一种可编程硬件芯片,它能以固定频率产生中断——定时中断。
1. 内核中的时间
内核必须在硬件帮助下才能计算和管理时间,硬件为内核提供一个系统定时器用来计算流逝的时间,系统定时器以某种频率自行触发时钟中断,该频率可以编程预定,称作节拍率(tick rate),发生时钟中断时内核会调用特殊的中断处理程序。
节拍率可以编程控制,那么两次连续时钟中断的间隔也可以得到,这个间隔称为节拍(tick),等于1/(tick rate)秒。
内核靠这种已知时钟中断间隔计算实际时间和系统运行时间。
2. 节拍率:HZ
系统定时器频率通过静态预处理定义,也就是HZ赫兹,在系统启动时按照HZ值对硬件进行设置。
内核在asm/param.h定义了这个值。最新内核定义了timekeeper结构保存实际时间,
定义在[linux/timekeeper_internal.h][https://github.com/torvalds/linux/blob/master/include/linux/timekeeper_internal.h]
|
|
提高节拍率的优劣
提高节拍率使时钟中断产生得更频繁,如此给整个系统带来如下好处:
- 更高的时钟中断解析度(resolution),可提高时间驱动事件的解析度
- 提高了时间驱动事件的准确度(accuracy)
高HZ的优劣
优势 | 劣势 |
---|---|
内核定时器能够以更高的频度和更高的准确度运行;依赖定时器执行的系统调用如poll()/select()能以更高的精度运行;对诸如资源消耗和系统运行时间等的测量精度更高;提高了进程抢占的准确度。 | 节拍率越高,时钟中断频率越高,导致系统负担越重,因为中断处理程序占用的处理器时间增多。 |
3. jiffies
全局变量jiffies保存了系统启动以来的节拍总数,jiffies一秒内增加的值为HZ,系统运行时间等于jiffies/HZ。
jiffies是无符号长整数(unsigned long),定义在linux/jiffies.h。
需要注意jiffies回绕的问题,比如32位无符号整型容易溢出,通常的处理是将其回绕(wrap around)到0,因此不要直接与jiffies值比较,应该使用内核提供的宏。
4. 硬时钟和定时器
体系结构提供两种设备进行计时:系统定时器和实时时钟。
- 实时时钟(RTC):用于持久存储系统时间,即便系统关闭,还可以依靠主板上的电池保持系统计时,它最主要的作用是启动时初始化xtime变量。
- 系统定时器:根本思想是提供一种周期性触发中断机制。x86采用可编程中断时钟PIT,其他时钟资源:时间戳计数TSC
5. 时钟中断处理程序
时钟中断处理程序分为两部分:体系结构相关部分和体系结构无关部分
绝大部分处理程序需要执行如下工作:
- 获取xtime_lock锁,保护对jiffies_64和墙上时间xtime的访问
- 需要时应答或重新设置系统时钟
- 周期性地使用墙上时间更新实时时钟
- 调用体系结构无关的时钟例程:tick_periodic()
tick_periodic()主要执行如下工作:
- jiffies_64加一
- 更新资源消耗的统计值,如当前进程所消耗的系统时间和用户时间
- 执行已经到期的动态定时器
- 执行sheduler_tick()函数
- 更新墙上时间xtime
- 计算平均负载值
以上操作每1/HZ秒发生一次,可以知道在x86上时钟中断处理程序每秒执行100次或1000次。
6. 实际时间
实际时间也就是墙上时间,内核版本3.3之前定义在kernel/time/timekeeping.c。
|
|
xtime存放了1970年一月一日(UTC)以来的时间。用户空间获取墙上时间的接口为gettimeofday(),定义在kernel/time.c,文件系统的实现代码存放访问时间戳需要使用xtime。
7. 定时器
定时器(动态定时器或内核定时器)是管理内核时间流逝的基础,定时器常常用于延迟特点时间后执行某些代码。
定时器由结构体timer_list表示,定义在linux/timer.h中。
内核提供了一组与定时器相关的接口用于定时器操作,声明在
8. 延迟执行
内核除了使用定时器或下半部机制以外,还需要其他方法来推迟执行任务,这种推迟通常发生在等待硬件完成某些工作,等待的时间往往非常短。
- 忙等待
- 短延迟
- schedule_timeout()