linux内核时间&定时器&延时

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) 任何 在未来某个时间点执行函数
相关推荐
snpgroupcn2 小时前
SAP S/4HANA迁移方法选哪种?选择性数据转换是否合适?企业需要考虑哪些关键因素!
运维·数据库·云计算
杜子不疼.2 小时前
【Linux】冯 • 诺依曼体系结构
linux·运维·服务器
门前灯3 小时前
Linux系统之msgexec 命令详解
linux·运维·服务器·msgexec
简一之学无止境3 小时前
Linux中信号量semaphore的实现
linux
東雪蓮☆4 小时前
MySQL 全量 + 增量备份脚本(RPM 安装)实践与问题解析
linux·运维·mysql
落羽的落羽4 小时前
【Linux系统】快速入门一些常用的基础指令
linux·服务器·人工智能·学习·机器学习·aigc
妮妮喔妮4 小时前
docker-compose端口冲突问题
运维·docker·容器
一只大头猿4 小时前
FinalShell 服务器远程连接工具
运维·服务器
kjl5365668 小时前
docker命令
运维·docker·容器