定时器是我们最常用到的功能,一般用来完成定时功能,本章我们就来学习一下 Linux 内核提供的定时器 API 函数,通过这些定时器 API 函数我们可以完成很多要求定时的应用。 Linux内核也提供了短延时函数,比如微秒、纳秒、毫秒延时函数,本章我们就来学习一下这些和时间有关的功能。
一、Linux时间管理和内核定时器介绍
1、内核时间管理
在FreeRTOS或者UCOS中需要一个硬件定时器提供系统时钟,一般使用Systick作为系统的时钟源。同理, Linux 要运行,也是需要一个系统时钟,Linux内核会选择Cortex-A7内核中的一个通用定时器来作为Linux系统的时钟源,对于Linux驱动编写来说,我们不需要深入研究怎么选择时钟源,怎么配置时钟源,Linux内核编写者已经编写好了,我们只需要会使用相应的API函数即可。
Linux 内核中有大量的函数需要时间管理,比如周期性的调度程序、延时程序、对于我们驱动编写者来说最常用的定时器。硬件定时器提供时钟源,时钟源的频率可以设置, 设置好以后就周期性的产生定时中断,系统使用定时中断来计时。中断周期性产生的频率就是系统频率,也叫做节拍率(tick rate)(有的资料也叫系统频率),比如 1000Hz, 100Hz 等等说的就是系统节拍率。系统节拍率是可以设置的,单位是 Hz,我们在编译 Linux 内核的时候可以通过图形化界面设置系统节拍率,按照如下路径打开配置界面:
> Kernel Features
-> Timer frequency (<choice> [=y])
Linux 内核会使用 CONFIG_HZ 来设置自己的系统时钟。打开文件 include/asm-generic/param.h,有如下内容:
HZ:表示1s时间内进入中断的次数,也叫做节拍数。通常说为节拍率。
选择高节拍率和低节拍率的优缺点 :
①、高节拍率会提高系统时间精度,如果采用 100Hz 的节拍率,时间精度就是 10ms,采用
1000Hz 的话时间精度就是 1ms,精度提高了 10 倍。高精度时钟的好处有很多,对于那些对时间要求严格的函数来说,能够以更高的精度运行,时间测量也更加准确。
②、高节拍率会导致中断的产生更加频繁,频繁的中断会加剧系统的负担, 1000Hz 和100Hz的系统节拍率相比,系统要花费 10 倍的“精力”去处理中断。中断服务函数占用处理器的时间增加,但是现在的处理器性能都很强大,所以采用 1000Hz 的系统节拍率并不会增加太大的负载压力。根据自己的实际情况,选择合适的系统节拍率,我们全部采用默认的 100Hz 系统节拍率。
Linux内核使用全局变量jiffies来记录系统从启动以来的系统节拍数,系统启动会将jiffies初始化为0,当然jiffies很重要,这也是我们设置定时器定时时间就是使用jiffies来完成的。jiffies 定义在文件 include/linux/jiffies.h 中,定义如下:
jiffies_64和jiffies是同一个东西, jiffies_64用于64位系统,jiffies用于32位系统,为了兼容不同的硬件,其实jiffies就是jiffies_64的低32位。
前面说了 HZ 表示每秒的节拍数, jiffies 表示系统运行的节拍数,所以 jiffies/HZ 就是系统运行时间,单位为秒。不管是 32 位还是 64 位的 jiffies,都有溢出的风险,溢出以后会重新从 0 开始计数,相当于绕回来了,因此有些资料也将这个现象也叫做绕回。假如 HZ 为最大值 1000 的时候, 32 位的 jiffies 只需要 49.7 天就发生了绕回,对于 64 位的 jiffies 来说大概需要5.8 亿年才能绕回,因此 jiffies_64 的绕回忽略不计。处理 32 位 jiffies 的绕回显得尤为重要,Linux 内核提供了几个 API 函数来处理绕回,如下如:
函数 | 描述(unkown为jiffies,known通常是对比的值) |
---|---|
time_after(unkown, known) | unkown 超过 known 的话, time_after 函数返回真,否则返回假。 |
time_before(unkown, known) | 如果 unkown 没有超过 known 的话 time_before 函数返回真,否则返回假 |
time_after_eq(unkown, known) | time_af |