Timing Measurement
Linux 内核的Timer必须要完成两种定时测量(timing measurement)
- 保存当前的日期时间
- 维护定时器(Timer)
保存当前时间提供给相关系统调用time(), ftime(),gettimeofday(),以便他们将日期时间返回给用户程序;维护定时器提醒内核某一段时间已经过去。要实现以上定时测量就离不开相关硬件的支持,下面看下X86体系下相关时钟和定时电路有哪些。
时钟和定时电路
下面仅列出X86体系结构下常见的时间相关硬件定时器、时钟。
- RTC :Real Time Clock
实时时钟, 依靠电池供电,集成在CMOS芯片上(结合NVRAM和RTC),可以得到日期和时间
- TSC :Time Stamp Counter
80×86处理器通过CLK引线,接收外部时钟信号,从Pentium开始 80×86处理器提供了一个TSC寄存器, 收到时钟信号后加一。可以通过rtdsc指令获取该值。因此TSC可以做为时钟设备。
又被称作8253/8254芯片, 这种设备不是通过振铃而是通过发出一种特殊中断,时钟中断通知内核一个时间间隔又过去了。时钟频率大约1.193182 MHz
- CPU Local APIC Timer: APIC timer
由于是CPU内部的振荡器所有避免了资源抢占,APIC timer分为三种模式: Periodic Mode(周期模式),One-Shot Mode(一次性模式),TSC-Deadline Mode(Deadline模式)
是Intel和Microsoft设计的硬件,用于替换PIT,RTC, 提供更高的频率, 包含一个64位的主计数器和3到32个32位或64位的比较器。工作模式分为: eriodic Mode(周期模式),One-Shot Mode(一次性模式)
- ACPI PMT: ACPI Power Management Timer
在所有基于 ACPI 的主板, 频率为 3.579545 MHz
关于 Timer Interrupt Sources 的比较
从上面表格也可以看到 这些硬件设备提供的功能非常简单:中断、计数,要基于这些硬件进行时间管理,还需要Linux Kernel做很多工作。
Linux 内核中的时间概念
内核依据硬件提供的时钟,以特定的频率称作 tick rate, 触发时钟中断,通过中断处理程序对其进行处理。
- 内核通过tick rate可以知道两次时钟中断的时间, 等于1/(tick rate)秒。
- 通过已知的时钟中断来跟踪实际时间(Wall time)和系统运行时间。
- 内核依据实际时间(Wall time)给用户空间提供一组相关系统调用。
- 系统运行时间-自系统启动后的时间,对于内核空间和用户空间都非常有用。
利用时钟中断进行的工作
- 更新系统时间
- 更新实际时间
- SMP系统中均衡调度程序中各处理器上运行队列
- 运行已超时的动态计数器
- 更新资源消耗和处理时间的统计值。
The Tick Rate :HZ
HZ是通过静态预处理定义的,在系统启动时按照HZ值对硬件进行设置。根据体系结构不通HZ值也不同。时钟中断周期为 1/HZ。
X86下默认HZ 100, 一个时钟中断周期为10ms,下面X86 CentOS 环境的设置。
[root@centosgpt asm-generic]# pwd
/usr/include/asm-generic
[root@centosgpt asm-generic]# cat param.h
#ifndef __ASM_GENERIC_PARAM_H
#define __ASM_GENERIC_PARAM_H
#ifndef HZ
#define HZ 100
#endif
- 高HZ的优点
- 内核计数器以更精细的分辨率( finer resolution)和更高的准确度(increased accuracy)运行。
- 测量(measurement)以更精细的分辨率记录
- 进程抢占更准确地发生
- 高HZ的缺点
- 更大的开销
- 处理器花费更多时间在时间中断上
- 更频繁执行中断打乱了处理器高速缓存,并增加耗电
- tickless
编译内核是可以通过CONFIG_HZ选项设置,系统可以根据需要动态调整时钟中断,在不需要终端的空档期, 不触发时钟中断,从而减少消耗。配置tickless
jiffies
全局变量jiffies用来记录系统启动以来产生的节拍的总数, 启动时,初始化为0, 每次时钟中断处理程序会递增。
jiffies与HZ, 一秒钟内时钟中断次数等于HZ,jiffies一秒中增加的值就是HZ,系统运行时间以秒为单位等于jiffies/HZ; 以秒为单位的时间转化为jiffies(seconds * HZ),
- The jiffies 变量总是一个 unsigned long类型, 32-bit 体系结构 32位, 64-bit 体系结构是64位
- 在100HZ情况下, 497天会溢出,1000HZ情况下49.7天会溢出,如果是64位的jiffies,由于数值非常大, 1000HZ情况下,大约 213503982334.6天会溢出。
- jiffies溢出后的回绕。
- 变量 extern u64 jiffies_64 ,jiffies读取jiffies_64低32位,通过get_jiffies_64()获得全部的64-bits值。
2038年问题 与jiffies回绕(wrap round)相关的问题,time_t被定义为32位的相关系统,在其上运行的应用如果使用POSIX时间的程序都将涉及2038年问题, 也就是到了2038年1月19日, time_t数字将无法工作。
定时器
- 定时器使用:设置一个超时时间, 指定超时发生后函数, 然后激活定时器就可以了。
- 定时器的竞争条件:定时器执行的与当前代码异步,所以可能存在潜在的竞争条件。
- 实现定时器:内核在时钟中断发生后执行定时器 , 定时器做为软中断在下半部上下文执行。
延迟
- 忙等待:需要一致循环,替代方案是使用cond_reched()
- 短延时:内核提供的三个延迟函数ji,他们没有使用jiffies
- void udelay(unsigned long usecs)
- void ndelay(unsigned long nsecs)
- void mdelay(unsigned long msecs)
- schedule_timeout() 让需要延时的任务睡眠到指定延时时间耗尽然后重新运行。
依赖于硬件提供的时钟中断和计数, 通过jiffies记录时钟tick, 完成对时间度量, 通过时间度量也完成了时间记录,定时器使用和延时的设置。
参考
What is the difference between CMOS and RTC?
Be First to Comment