1、内核时间
1.1节拍率(tick rate)
在内核中指定了一个变量HZ,内核初始化的时候会根据这个值确定定时器的节拍率。
HZ定义在 include/asm-generic/param.h
bash
-> Kernel Features -> Timer frequency ( [=y])

- 高节拍内核定时器能够以更高的频率和更高的准确度运行,依赖定时器执行的系统调用,比如poll()和select(),运行的精度更高
- 高节拍率提高进程抢占的准确度(缩短了调度延时,如果进程还剩2ms时间片,在10ms的调度周期下,进程会多运行8ms。 由于耽误了抢占,对于一些对时间要求严格的任务会产生影响)
- 节拍率越高,系统负担越重。
1.2 jiffies
Linux 内核使用全局变量 jiffies 来记录系统从启动以来的系统节拍数,系统启动的时候会将 jiffies 初始化为 0,每次时钟中断处理程序都会增加该变量的值,jiffies 定义在文件include/linux/jiffies.h 中
c
extern u64 __jiffy_data jiffies_64;
extern unsigned long volatile __jiffy_data jiffies;
因此在32位体系结构上是32位,而在64位体系上是64位。对于32位的jiffies,如果HZ为1000,49.7天后会溢出。虽然溢出的情况不常见,但程序在检测超时时仍然可能因为回绕而导致错误。linux提供了4个宏来比较节拍计数,它们能正确地处理节拍计数回绕。
2、内核短延时
2.1
c
#include <linux/jiffies.h>
#define time_after(unknown, known) // unknow > known
#define time_before(unknown, known) // unknow < known
#define time_after_eq(unknown, known) // unknow >= known
#define time_before_eq(unknown, known) // unknow <= known
unkown 通常为 jiffies,known 通常是需要对比的值
如果 unkown 超过 known 的话,time_after 函数返回真,否则返回假。如果 unkown 没有超
过 known 的话 time_before 函数返回真,否则返回假。time_after_eq 函数和 time_after 函数类似,
只是多了判断等于这个条件。同理,time_before_eq 函数和 time_before 函数也类似。
jiffies和msecs以及usecs的转换:
c
unsigned int jiffies_to_msecs(const unsigned long);
unsigned int jiffies_to_usecs(const unsigned long);
unsigned long msecs_to_jiffies(const unsigned int m);
unsigned long usecs_to_jiffies(const unsigned int u);
2.2
阻塞
c
#include <linux/delay.h>
void ndelay(unsigned long nsecs); /*延迟纳秒 */
void udelay(unsigned long usecs); /*延迟微秒 */
void mdelay(unsigned long msecs); /*延迟毫秒 */
3 内核休眠延时
非原子环境的睡眠,用于不需要特别精确的长短延时
c
#include <linux/delay.h>
void usleep_range(unsigned long min, unsigned long max);//用于微秒级别的休眠
void msleep(unsigned int msecs); //用于毫秒级别的休眠
unsigned long msleep_interruptible(unsigned int msecs);
//与 msleep 类似,但任务进入 TASK_INTERRUPTIBLE 状态。如果在休眠期间接收到信号,它会提前被唤醒。
void ssleep(unsigned int seconds);
//用于秒级别的休眠。msleep 的宏实现,参数单位是秒.任务同样处于 TASK_UNINTERRUPTIBLE 状态。
4、内核定时器
可以理解为ucos的软定时器。
- 内核定时器并不是周期性运行的,超时以后就会自动关闭。此如果想要实现周期性定时,那么就需要在定时回调处理函数中重新开启定时器
include/linux/timer.h
c
struct timer_list {
struct list_head entry;
unsigned long expires; /* 定时器超时时间,单位是节拍数 */
struct tvec_base *base;
void (*function)(unsigned long); /* 定时处理函数 */
unsigned long data; /* 要传递给 function 函数的参数 */
int slack;
};
4.1 API函数接口
函数 | 描述 |
---|---|
timer_list | 定义定时器timer_list 变量 |
init_timer | 初始化 timer_list 类型变量 |
add_timer | 用于向 Linux 内核注册定时器,注册以后,定时器就会开始运行 |
del_timer | 删除一个定时器 |
del_timer_sync | 等待其他处理器使用完定时器再删除,del_timer_sync 不能使用在中断上下文中 |
mod_timer | 于修改定时值,如果定时器还没有激活,mod_timer 函数会激活定时器 |
c
timer.function = function; /* 设置定时处理函数 */
timer.data = (unsigned long)&dev; /* 将设备结构体作为参数 */
timer.expires=jffies + msecs_to_jiffies(2000);//
add_timer(&timer); /* 启动定时器//mod_timer(&timer, jiffies + msecs_to_jiffies(val));
4、schedule_timeout()
c
set_current_state(TASK_INTERRUPTIBLE); /* 将任务设置为可中断睡眠状态 */
schedule_timeout(s *HZ); /* 睡眠,s秒后唤醒 */
5、高精度定时器(hrtimers)延时(未完)
总结
机制 | 精度 | 是否让出 CPU | 适用上下文 | 主要用途 |
---|---|---|---|---|
ndelay/udelay/mdelay | 高 | 否(忙等待) | 任何(慎用) | 极短延时,原子上下文 |
usleep_range | 中 | 是 | 进程上下文 | 不精确的短延时,电源优化 |
msleep/ssleep | 低 | 是 | 进程上下文 | 长时间的休眠 |
hrtimer | 非常高 | 是 | 任何 | 高精度延时或定时任务 |
内核定时器 | 低(基于HZ) | 是 | 任何 | 在未来某个时间点执行函数 |