news 2026/7/18 17:18:25

Linux内核定时器

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核定时器

一、基础概念

1. 内核定时器是什么

内核定时器是基于系统时钟节拍(jiffies)的软件定时器,属于一次性定时器(超时执行一次,不会自动循环),用于驱动中延时、周期性检测、超时处理(看门狗、按键消抖、设备超时、LED闪烁等)。

  • 分类:
    1. 传统低精度定时器:timer_list(基于jiffies,毫秒级,最常用)
    2. 高精度定时器:hrtimer(基于硬件定时器,纳秒/微秒级,需要内核开启CONFIG_HIGH_RES_TIMERS)

2. 核心基础:jiffies

  1. 内核开机初始化全局变量unsigned long jiffies,记录系统启动以来的时钟节拍总数。
  2. HZ:每秒产生多少次时钟中断,配置文件定义(嵌入式常见 HZ=100 / 250 / 1000)
    • HZ=100:1节拍=10ms
    • HZ=1000:1节拍=1ms
  3. 时间与节拍互转宏
// 毫秒转节拍msecs_to_jiffies(ms)// 节拍转毫秒jiffies_to_msecs(j)// 秒转节拍secs_to_jiffies(s)
  1. 超时判断标准写法(避免溢出)
// 判断是否超时:当前jiffies >= 超时点if(time_after(jiffies,timer_expire))// 未超时if(time_before(jiffies,timer_expire))

二、传统定时器 timer_list(驱动90%场景使用)

1. 结构体定义

#include<linux/timer.h>structtimer_list{structhlist_nodeentry;unsignedlongexpires;// 超时jiffies值void(*function)(unsignedlong);// 超时回调函数unsignedlongdata;// 传给回调的参数// 内核隐藏字段};

2. 标准使用四步流程

① 定义定时器对象

全局/局部(建议全局,栈空间有限)

structtimer_listled_timer;

② 编写超时回调函数

重点限制

  1. 回调运行在软中断上下文,不能睡眠!
    • 禁止:msleep()kmalloc(GFP_KERNEL)、信号量down()
    • 允许:mdelay()kmalloc(GFP_ATOMIC)、自旋锁
  2. 执行速度要快,不要做耗时操作
voidled_timer_func(unsignedlongarg){// 示例:翻转LED状态gpio_set_value(arg,!gpio_get_value(arg));// 如果要循环定时,必须手动重新启动定时器mod_timer(&led_timer,jiffies+msecs_to_jiffies(500));}

③ 初始化定时器(2种方式)

方式1:编译期宏(推荐)

#defineLED_GPIO10timer_setup(&led_timer,led_timer_func,LED_GPIO);

方式2:运行时初始化(旧内核init_timer已废弃)

init_timer(&led_timer);led_timer.function=led_timer_func;led_timer.data=LED_GPIO;

④ 启动/修改/删除定时器

  1. 启动/重置定时器mod_timer(最常用,无需判断是否已注册)
// 500ms后执行回调mod_timer(&led_timer,jiffies+msecs_to_jiffies(500));
  1. 删除定时器
// 安全删除:等待正在执行的回调函数跑完再返回del_timer_sync(&led_timer);// 非等待删除:不阻塞,可能回调还在运行del_timer(&led_timer);

驱动卸载函数必须调用del_timer_sync,防止卸载后定时器野指针访问。

3. 完整极简示例(LED闪烁驱动片段)

#include<linux/timer.h>#include<linux/gpio/consumer.h>structtimer_listled_timer;voidtimer_cb(unsignedlonggpio){gpio_set_value(gpio,!gpio_get_value(gpio));mod_timer(&led_timer,jiffies+msecs_to_jiffies(500));}staticintdrv_probe(...){intgpio=12;timer_setup(&led_timer,timer_cb,gpio);mod_timer(&led_timer,jiffies+msecs_to_jiffies(100));return0;}staticintdrv_remove(...){del_timer_sync(&led_timer);return0;}

4. timer_list 核心API汇总

API作用
timer_setup初始化定时器
mod_timer(timer, expires)设置/修改超时时间,自动加入内核链表
del_timer删除定时器,不阻塞
del_timer_sync阻塞删除,等待回调执行完毕(卸载必用)
timer_pending(timer)判断定时器是否已经挂入队列(未超时)

5. 优缺点

优点

  • API简单,所有内核版本兼容
  • 资源开销极小,适合大量简单定时场景(按键消抖、状态轮询、LED闪烁)

缺点

  • 精度受HZ限制,HZ=100时最小定时10ms,无法微秒级延时
  • 回调软中断上下文,不能睡眠

三、高精度定时器 hrtimer(微秒/纳秒定时)

1. 适用场景

需要高精度定时:PWM控制、编码器采样、高频信号采集、精准延时。
头文件:#include <linux/hrtimer.h>

2. 核心结构体

structhrtimer{structtimerqueue_nodenode;ktime_texpires;// 高精度时间,ktime_t类型enumhrtimer_restart(*function)(structhrtimer*);// ...};
  • ktime_t:内核高精度时间类型,单位纳秒
  • 回调返回值控制是否循环:
    • HRTIMER_NORESTART:一次性定时
    • HRTIMER_RESTART:自动重新定时

3. 基础使用步骤

  1. 定义
structhrtimerhrt;
  1. 回调函数(返回重启标记)
enumhrtimer_restarthrt_cb(structhrtimer*timer){// 定时任务// 重置超时时间,500mshrtimer_forward_now(timer,ms_to_ktime(500));returnHRTIMER_RESTART;}
  1. 初始化+启动
// 初始化:CLOCK_MONOTONIC 不受系统时间修改影响hrtimer_init(&hrt,CLOCK_MONOTONIC,HRTIMER_MODE_REL);hrt.function=hrt_cb;// 启动,相对当前时间500mshrtimer_start(&hrt,ms_to_ktime(500),HRTIMER_MODE_REL);
  1. 卸载销毁
hrtimer_cancel(&hrt);// 同步等待回调结束

4. 时间转换宏

ms_to_ktime(ms)// 毫秒转ktimens_to_ktime(ns)// 纳秒转ktimektime_to_ms(t)// ktime转毫秒

5. 优缺点

优点:微秒/纳秒级高精度,独立硬件定时器,不受HZ限制
缺点:内核需要开启高精度定时器配置,开销比timer_list大


四、定时器上下文与关键禁忌(高频踩坑点)

1. 上下文区分

  1. timer_list回调:软中断上下文
    • 无进程上下文,无current,不能睡眠
  2. hrtimer回调:高优先级软中断,同样禁止睡眠

2. 绝对禁止操作

  1. 睡眠类函数:msleepssleepwait_event、互斥锁mutex_lock
  2. 分配内存阻塞:kmalloc(GFP_KERNEL),要用GFP_ATOMIC
  3. 回调内长时间循环阻塞系统中断

3. 循环定时标准规范

不要在回调里频繁创建/销毁定时器,两种标准方案:

  1. timer_list:回调末尾调用mod_timer手动续期
  2. hrtimer:返回HRTIMER_RESTART自动续期

4. 竞态保护要点

  1. 卸载函数必须用del_timer_sync / hrtimer_cancel,防止回调访问已释放设备
  2. 如果定时器访问共享变量,需要加自旋锁(不能用互斥锁)

五、内核延时函数(和定时器区分开,很多人混淆)

定时器是异步延时(到点执行函数,不阻塞当前代码)
下面是同步阻塞延时,直接卡主当前线程:

  1. 忙等待(原子上下文/中断里用,耗CPU)
mdelay(ms);// 毫秒忙等udelay(us);// 微秒忙等ndelay(ns);// 纳秒忙等
  1. 睡眠等待(进程上下文,可休眠,释放CPU)
msleep(ms);ssleep(s);

六、选型对比表(驱动开发选型指南)

类型精度上下文是否自动循环适用场景
timer_listHZ节拍级(1~10ms)软中断,不能睡眠需手动mod_timerLED闪烁、按键消抖、设备轮询、低精度超时
hrtimer微秒/纳秒级高优先级软中断支持自动重启PWM、电机采样、高频定时、精准控制
msleep毫秒级阻塞仅进程上下文无,同步阻塞初始化短延时
mdelay忙等阻塞任意上下文中断内极短延时

七、常见面试/调试考点

  1. mod_timer和add_timer区别
    • add_timer只能启动未注册的定时器;mod_timer兼容启动+修改,推荐只用mod_timer
  2. del_timer和del_timer_sync区别
    • sync会等待正在运行的回调结束,卸载函数必须用,防止use after free
  3. 定时器回调为什么不能睡眠
    • 软中断没有进程调度实体,睡眠会造成内核死锁/卡死
  4. jiffies溢出问题怎么处理
    • 必须使用time_before/time_after,不能直接用 > < 判断
  5. 循环定时两种实现方式
    • timer_list:mod_timer;hrtimer:返回HRTIMER_RESTART
  6. sysfs LED控制和定时器结合实战
    用timer_list定时翻转gpio,配合brightness节点启停定时器,就是你前面那条led命令的底层驱动逻辑。

八、Linux 内核 timer_list 定时器完整驱动示例

功能:加载模块后,每500ms翻转一次LED(GPIO模拟),卸载时安全销毁定时器。

1、timer_demo.c

#include<linux/module.h>#include<linux/kernel.h>#include<linux/init.h>#include<linux/timer.h>#include<linux/gpio/consumer.h>#include<linux/delay.h>// 自定义LED GPIO,根据你的开发板修改#defineLED_GPIO_NUM10// 定时器结构体staticstructtimer_listled_timer;// LED状态全局标记staticbool led_status=false;// 定时器超时回调函数// 注意:运行在软中断上下文,禁止睡眠、mutex_lock、msleepstaticvoidled_timer_callback(unsignedlongarg){intgpio=arg;// 翻转LED状态led_status=!led_status;gpio_set_value(gpio,led_status);// 重新启动定时器,实现循环定时 500msmod_timer(&led_timer,jiffies+msecs_to_jiffies(500));}staticint__inittimer_demo_init(void){intret;// 1. 申请GPIOret=gpio_request(LED_GPIO_NUM,"timer_led");if(ret<0){pr_err("gpio request failed, ret = %d\n",ret);returnret;}// 设置GPIO为输出,初始低电平(灭灯)gpio_direction_output(LED_GPIO_NUM,0);led_status=false;// 2. 初始化定时器:绑定回调、传参GPIO号timer_setup(&led_timer,led_timer_callback,LED_GPIO_NUM);// 3. 启动定时器,100ms后第一次触发mod_timer(&led_timer,jiffies+msecs_to_jiffies(100));pr_info("timer demo module loaded, led gpio = %d\n",LED_GPIO_NUM);pr_info("current jiffies = %lu, HZ = %d\n",jiffies,HZ);return0;}staticvoid__exittimer_demo_exit(void){// 同步删除定时器:等待正在执行的回调完全结束再返回del_timer_sync(&led_timer);// 熄灭LED,释放GPIOgpio_set_value(LED_GPIO_NUM,0);gpio_free(LED_GPIO_NUM);pr_info("timer demo module unloaded\n");}module_init(timer_demo_init);module_exit(timer_demo_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("Driver Demo");MODULE_DESCRIPTION("Linux kernel timer_list simple demo");MODULE_VERSION("1.0");

2、Makefile

obj-m += timer_demo.o # 替换成你的内核源码路径 KERNELDIR ?= /lib/modules/$(shell uname -r)/build PWD := $(shell pwd) all: $(MAKE) -C $(KERNELDIR) M=$(PWD) modules clean: $(MAKE) -C $(KERNELDIR) M=$(PWD) clean

3、编译、加载、测试命令

# 编译make# 加载驱动sudoinsmod timer_demo.ko# 查看内核打印dmesg-w# 查看当前jiffies验证节拍cat/proc/jiffies# 卸载驱动sudormmod timer_demo

4、核心知识点注释梳理

1. 定时器四步标准流程

  1. timer_setup:初始化定时器(绑定回调+传参)
  2. mod_timer:启动/刷新超时时间
  3. 回调内部再次调用mod_timer实现循环
  4. 卸载函数del_timer_sync安全销毁

2. 回调函数硬性限制(极易踩坑)

  1. 软中断上下文,不能睡眠
    • 禁止:msleep()mutex_lock()wait_eventkmalloc(GFP_KERNEL)
    • 允许:mdelay()、自旋锁spin_lockkmalloc(GFP_ATOMIC)
  2. 回调执行逻辑必须简短,不能长时间循环阻塞系统

3. API区别

  • mod_timer:通用接口,定时器未启动则新建,已启动则修改超时,推荐只用它
  • del_timer:异步删除,不阻塞,可能出现回调正在访问模块内存
  • del_timer_sync:阻塞等待回调执行完毕,驱动卸载必须使用

4. 时间转换宏

msecs_to_jiffies(500)// 500ms 转系统节拍secs_to_jiffies(2)// 2秒转节拍jiffies_to_msecs(jiffies)// 节拍转毫秒

5、高精度定时器 hrtimer 简易扩展片段(对比参考)

如果需要微秒级定时,替换为hrtimer核心代码:

#include<linux/hrtimer.h>staticstructhrtimerhrt;enumhrtimer_restarthrt_callback(structhrtimer*timer){// 翻转LEDled_status=!led_status;gpio_set_value(LED_GPIO_NUM,led_status);// 延后500ms自动重启hrtimer_forward_now(timer,ms_to_ktime(500));returnHRTIMER_RESTART;}// 初始化hrtimer_init(&hrt,CLOCK_MONOTONIC,HRTIMER_MODE_REL);hrt.function=hrt_callback;hrtimer_start(&hrt,ms_to_ktime(500),HRTIMER_MODE_REL);// 卸载销毁hrtimer_cancel(&hrt);

6、常见报错排查

  1. gpio_request failed:GPIO被其他驱动占用,更换GPIO号
  2. 卸载模块内核Oops:忘记使用del_timer_sync,定时器回调访问已释放内存
  3. 定时器不循环:回调内部缺少mod_timer
  4. 睡眠锁死内核:回调中调用msleep/mutex等睡眠函数
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/18 17:18:23

2026年揭秘:市面上知名矫形头盔门店,真实口碑究竟如何?

在现代育儿观念中&#xff0c;宝宝头型美观日益受到重视&#xff0c;矫形头盔市场也愈发火热。2026年的今天&#xff0c;市面上知名矫形头盔门店的真实口碑到底怎样&#xff0c;下面为你深度剖析。行业现状与痛点行业报告显示&#xff0c;目前矫形头盔市场鱼龙混杂。消费者存在…

作者头像 李华
网站建设 2026/7/18 17:16:52

关于前端vue框架项目环境搭建-node-sass Python Vscodenode-gyp

目录 一.安装npm 1、nvm 下载安装 2、nvm 环境配置 3、nvm 下载 node 二.环境编译 1、配置镜像地址 2、加载依赖 3、工具缺失解决方案 4、运行 三.异常记录 异常1&#xff1a;树异常&#xff08;补充未测试&#xff09; 异常2&#xff1a;sass 报错 异常3&#xf…

作者头像 李华
网站建设 2026/7/18 17:15:57

AI 生成的设计总是一眼假?这个工具说:拒绝「AI 味」

当所有大模型都被训练成同一副审美&#xff0c;生成出来的页面自然千篇一律。Hallmark 偏要反着来——它是一款设计技能&#xff0c;专治「一眼就能看出是 AI 写的」那种页面。它是谁&#xff1f; Hallmark 是一款为 Claude Code、Cursor、Codex 量身打造的设计技能&#xff08…

作者头像 李华
网站建设 2026/7/18 17:14:48

米面粮油刚需款工会生日福利货源,线上兑换适配供应链拆解

随着小程序自主兑换工会福利普及&#xff0c;传统线下囤货分发模式逐步淘汰&#xff0c;适配线上兑换的米面粮油刚需款工会生日福利货源&#xff0c;和线下批量囤货货源标准存在明显区别。本文针对线上弹性福利场景&#xff0c;分析生日粮油货源选型与供应链搭建方案。 一、线上…

作者头像 李华
网站建设 2026/7/18 17:14:45

RSSHub Radar:现代网页环境下的RSS订阅解决方案

1. RSS订阅的困境与RSSHub Radar的诞生 在信息爆炸的时代&#xff0c;我们每天都被各种平台推送的内容淹没。作为一个长期关注技术动态的开发者&#xff0c;我发现自己越来越依赖RSS&#xff08;Really Simple Syndication&#xff09;这种古老但高效的信息获取方式。它能让我在…

作者头像 李华