Linux内核定时器

📅 2026/7/18 17:18:29
Linux内核定时器
一、基础概念1. 内核定时器是什么内核定时器是基于系统时钟节拍(jiffies)的软件定时器属于一次性定时器超时执行一次不会自动循环用于驱动中延时、周期性检测、超时处理看门狗、按键消抖、设备超时、LED闪烁等。分类传统低精度定时器timer_list基于jiffies毫秒级最常用高精度定时器hrtimer基于硬件定时器纳秒/微秒级需要内核开启CONFIG_HIGH_RES_TIMERS2. 核心基础jiffies内核开机初始化全局变量unsigned long jiffies记录系统启动以来的时钟节拍总数。HZ每秒产生多少次时钟中断配置文件定义嵌入式常见 HZ100 / 250 / 1000HZ1001节拍10msHZ10001节拍1ms时间与节拍互转宏// 毫秒转节拍msecs_to_jiffies(ms)// 节拍转毫秒jiffies_to_msecs(j)// 秒转节拍secs_to_jiffies(s)超时判断标准写法避免溢出// 判断是否超时当前jiffies 超时点if(time_after(jiffies,timer_expire))// 未超时if(time_before(jiffies,timer_expire))二、传统定时器 timer_list驱动90%场景使用1. 结构体定义#includelinux/timer.hstructtimer_list{structhlist_nodeentry;unsignedlongexpires;// 超时jiffies值void(*function)(unsignedlong);// 超时回调函数unsignedlongdata;// 传给回调的参数// 内核隐藏字段};2. 标准使用四步流程① 定义定时器对象全局/局部建议全局栈空间有限structtimer_listled_timer;② 编写超时回调函数重点限制回调运行在软中断上下文不能睡眠禁止msleep()、kmalloc(GFP_KERNEL)、信号量down()允许mdelay()、kmalloc(GFP_ATOMIC)、自旋锁执行速度要快不要做耗时操作voidled_timer_func(unsignedlongarg){// 示例翻转LED状态gpio_set_value(arg,!gpio_get_value(arg));// 如果要循环定时必须手动重新启动定时器mod_timer(led_timer,jiffiesmsecs_to_jiffies(500));}③ 初始化定时器2种方式方式1编译期宏推荐#defineLED_GPIO10timer_setup(led_timer,led_timer_func,LED_GPIO);方式2运行时初始化旧内核init_timer已废弃init_timer(led_timer);led_timer.functionled_timer_func;led_timer.dataLED_GPIO;④ 启动/修改/删除定时器启动/重置定时器mod_timer最常用无需判断是否已注册// 500ms后执行回调mod_timer(led_timer,jiffiesmsecs_to_jiffies(500));删除定时器// 安全删除等待正在执行的回调函数跑完再返回del_timer_sync(led_timer);// 非等待删除不阻塞可能回调还在运行del_timer(led_timer);驱动卸载函数必须调用del_timer_sync防止卸载后定时器野指针访问。3. 完整极简示例LED闪烁驱动片段#includelinux/timer.h#includelinux/gpio/consumer.hstructtimer_listled_timer;voidtimer_cb(unsignedlonggpio){gpio_set_value(gpio,!gpio_get_value(gpio));mod_timer(led_timer,jiffiesmsecs_to_jiffies(500));}staticintdrv_probe(...){intgpio12;timer_setup(led_timer,timer_cb,gpio);mod_timer(led_timer,jiffiesmsecs_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限制HZ100时最小定时10ms无法微秒级延时回调软中断上下文不能睡眠三、高精度定时器 hrtimer微秒/纳秒定时1. 适用场景需要高精度定时PWM控制、编码器采样、高频信号采集、精准延时。头文件#include linux/hrtimer.h2. 核心结构体structhrtimer{structtimerqueue_nodenode;ktime_texpires;// 高精度时间ktime_t类型enumhrtimer_restart(*function)(structhrtimer*);// ...};ktime_t内核高精度时间类型单位纳秒回调返回值控制是否循环HRTIMER_NORESTART一次性定时HRTIMER_RESTART自动重新定时3. 基础使用步骤定义structhrtimerhrt;回调函数返回重启标记enumhrtimer_restarthrt_cb(structhrtimer*timer){// 定时任务// 重置超时时间500mshrtimer_forward_now(timer,ms_to_ktime(500));returnHRTIMER_RESTART;}初始化启动// 初始化CLOCK_MONOTONIC 不受系统时间修改影响hrtimer_init(hrt,CLOCK_MONOTONIC,HRTIMER_MODE_REL);hrt.functionhrt_cb;// 启动相对当前时间500mshrtimer_start(hrt,ms_to_ktime(500),HRTIMER_MODE_REL);卸载销毁hrtimer_cancel(hrt);// 同步等待回调结束4. 时间转换宏ms_to_ktime(ms)// 毫秒转ktimens_to_ktime(ns)// 纳秒转ktimektime_to_ms(t)// ktime转毫秒5. 优缺点优点微秒/纳秒级高精度独立硬件定时器不受HZ限制缺点内核需要开启高精度定时器配置开销比timer_list大四、定时器上下文与关键禁忌高频踩坑点1. 上下文区分timer_list回调软中断上下文无进程上下文无current不能睡眠hrtimer回调高优先级软中断同样禁止睡眠2. 绝对禁止操作睡眠类函数msleep、ssleep、wait_event、互斥锁mutex_lock分配内存阻塞kmalloc(GFP_KERNEL)要用GFP_ATOMIC回调内长时间循环阻塞系统中断3. 循环定时标准规范不要在回调里频繁创建/销毁定时器两种标准方案timer_list回调末尾调用mod_timer手动续期hrtimer返回HRTIMER_RESTART自动续期4. 竞态保护要点卸载函数必须用del_timer_sync / hrtimer_cancel防止回调访问已释放设备如果定时器访问共享变量需要加自旋锁不能用互斥锁五、内核延时函数和定时器区分开很多人混淆定时器是异步延时到点执行函数不阻塞当前代码下面是同步阻塞延时直接卡主当前线程忙等待原子上下文/中断里用耗CPUmdelay(ms);// 毫秒忙等udelay(us);// 微秒忙等ndelay(ns);// 纳秒忙等睡眠等待进程上下文可休眠释放CPUmsleep(ms);ssleep(s);六、选型对比表驱动开发选型指南类型精度上下文是否自动循环适用场景timer_listHZ节拍级(1~10ms)软中断不能睡眠需手动mod_timerLED闪烁、按键消抖、设备轮询、低精度超时hrtimer微秒/纳秒级高优先级软中断支持自动重启PWM、电机采样、高频定时、精准控制msleep毫秒级阻塞仅进程上下文无同步阻塞初始化短延时mdelay忙等阻塞任意上下文无中断内极短延时七、常见面试/调试考点mod_timer和add_timer区别add_timer只能启动未注册的定时器mod_timer兼容启动修改推荐只用mod_timerdel_timer和del_timer_sync区别sync会等待正在运行的回调结束卸载函数必须用防止use after free定时器回调为什么不能睡眠软中断没有进程调度实体睡眠会造成内核死锁/卡死jiffies溢出问题怎么处理必须使用time_before/time_after不能直接用 判断循环定时两种实现方式timer_listmod_timerhrtimer返回HRTIMER_RESTARTsysfs LED控制和定时器结合实战用timer_list定时翻转gpio配合brightness节点启停定时器就是你前面那条led命令的底层驱动逻辑。八、Linux 内核 timer_list 定时器完整驱动示例功能加载模块后每500ms翻转一次LEDGPIO模拟卸载时安全销毁定时器。1、timer_demo.c#includelinux/module.h#includelinux/kernel.h#includelinux/init.h#includelinux/timer.h#includelinux/gpio/consumer.h#includelinux/delay.h// 自定义LED GPIO根据你的开发板修改#defineLED_GPIO_NUM10// 定时器结构体staticstructtimer_listled_timer;// LED状态全局标记staticbool led_statusfalse;// 定时器超时回调函数// 注意运行在软中断上下文禁止睡眠、mutex_lock、msleepstaticvoidled_timer_callback(unsignedlongarg){intgpioarg;// 翻转LED状态led_status!led_status;gpio_set_value(gpio,led_status);// 重新启动定时器实现循环定时 500msmod_timer(led_timer,jiffiesmsecs_to_jiffies(500));}staticint__inittimer_demo_init(void){intret;// 1. 申请GPIOretgpio_request(LED_GPIO_NUM,timer_led);if(ret0){pr_err(gpio request failed, ret %d\n,ret);returnret;}// 设置GPIO为输出初始低电平(灭灯)gpio_direction_output(LED_GPIO_NUM,0);led_statusfalse;// 2. 初始化定时器绑定回调、传参GPIO号timer_setup(led_timer,led_timer_callback,LED_GPIO_NUM);// 3. 启动定时器100ms后第一次触发mod_timer(led_timer,jiffiesmsecs_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、Makefileobj-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) clean3、编译、加载、测试命令# 编译make# 加载驱动sudoinsmod timer_demo.ko# 查看内核打印dmesg-w# 查看当前jiffies验证节拍cat/proc/jiffies# 卸载驱动sudormmod timer_demo4、核心知识点注释梳理1. 定时器四步标准流程timer_setup初始化定时器绑定回调传参mod_timer启动/刷新超时时间回调内部再次调用mod_timer实现循环卸载函数del_timer_sync安全销毁2. 回调函数硬性限制极易踩坑软中断上下文不能睡眠禁止msleep()、mutex_lock()、wait_event、kmalloc(GFP_KERNEL)允许mdelay()、自旋锁spin_lock、kmalloc(GFP_ATOMIC)回调执行逻辑必须简短不能长时间循环阻塞系统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核心代码#includelinux/hrtimer.hstaticstructhrtimerhrt;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.functionhrt_callback;hrtimer_start(hrt,ms_to_ktime(500),HRTIMER_MODE_REL);// 卸载销毁hrtimer_cancel(hrt);6、常见报错排查gpio_request failedGPIO被其他驱动占用更换GPIO号卸载模块内核Oops忘记使用del_timer_sync定时器回调访问已释放内存定时器不循环回调内部缺少mod_timer睡眠锁死内核回调中调用msleep/mutex等睡眠函数