Linux 驱动中断与定时完整篇:从 request_threaded_irq 到hrtimer 选型与排障

外设「偶发丢中断」、下半部太重把调度延迟打爆、或用 mod_timer 做 100µs 周期控制却怎么都不准------根因通常是 硬中断上下文约束被打破 ,或 定时器类型选错。驱动侧真正该问的是:这件事必须在硬中断里做完吗?能睡吗?精度要到 jiffies 还是要 hrtimer/硬件定时器?

本文把 request_threaded_irq、主 handler/thread_fn 分工、timer_listhrtimer、DT 取中断号、观测与卸载竞态合成一篇可验证闭环。源 chapter(081--083、085--087)为提纲;正文按主线 kernel/irq/kernel/time/ 真实路径重写。


源码锚点

路径 作用
include/linux/interrupt.h request_irqrequest_threaded_irqIRQF_*irqreturn_t
kernel/irq/manage.c 申请/释放 IRQ、线程化中断安装
kernel/irq/handle.c 一带 通用中断处理入口(与架构入口衔接)
include/linux/timer.h timer_listtimer_setupmod_timerdel_timer_sync
kernel/time/timer.c 低分辨率定时器轮
include/linux/hrtimer.h / kernel/time/hrtimer.c 高分辨率定时器
include/linux/workqueue.h 可睡眠重活的投递目标
Documentation/core-api/irq/ IRQ 域、请求与线程化说明

线程化中断 API:

c 复制代码
/* include/linux/interrupt.h */
int request_threaded_irq(unsigned int irq,
			 irq_handler_t handler,     /* 硬中断;可 NULL */
			 irq_handler_t thread_fn,   /* IRQ 线程;可睡眠 */
			 unsigned long flags,
			 const char *name, void *dev);

/* 返回值:IRQ_HANDLED / IRQ_WAKE_THREAD / IRQ_NONE */

两类定时器骨架:

c 复制代码
/* jiffies 级 */
timer_setup(&dev->timer, my_timer_cb, 0);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(10));
/* 卸载:del_timer_sync(&dev->timer); */

/* 高分辨率 */
hrtimer_init(&dev->hr, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
dev->hr.function = my_hr_cb;
hrtimer_start(&dev->hr, ms_to_ktime(1), HRTIMER_MODE_REL);
/* 周期:回调里 hrtimer_forward_now + return HRTIMER_RESTART
   卸载:hrtimer_cancel(&dev->hr); */

从 DT/platform 取 Linux IRQ 号:

c 复制代码
irq = platform_get_irq(pdev, 0);
/* 或 irq_of_parse_and_map(np, 0) */
if (irq < 0)
	return irq;
err = request_threaded_irq(irq, my_hard, my_thread,
			   IRQF_ONESHOT, "mydrv", priv);

调用链

硬件中断到驱动线程(主路径)

#mermaid-svg-I86EcudYLT8bzTlH{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-I86EcudYLT8bzTlH .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-I86EcudYLT8bzTlH .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-I86EcudYLT8bzTlH .error-icon{fill:#552222;}#mermaid-svg-I86EcudYLT8bzTlH .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-I86EcudYLT8bzTlH .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-I86EcudYLT8bzTlH .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-I86EcudYLT8bzTlH .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-I86EcudYLT8bzTlH .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-I86EcudYLT8bzTlH .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-I86EcudYLT8bzTlH .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-I86EcudYLT8bzTlH .marker{fill:#333333;stroke:#333333;}#mermaid-svg-I86EcudYLT8bzTlH .marker.cross{stroke:#333333;}#mermaid-svg-I86EcudYLT8bzTlH svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-I86EcudYLT8bzTlH p{margin:0;}#mermaid-svg-I86EcudYLT8bzTlH .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-I86EcudYLT8bzTlH .cluster-label text{fill:#333;}#mermaid-svg-I86EcudYLT8bzTlH .cluster-label span{color:#333;}#mermaid-svg-I86EcudYLT8bzTlH .cluster-label span p{background-color:transparent;}#mermaid-svg-I86EcudYLT8bzTlH .label text,#mermaid-svg-I86EcudYLT8bzTlH span{fill:#333;color:#333;}#mermaid-svg-I86EcudYLT8bzTlH .node rect,#mermaid-svg-I86EcudYLT8bzTlH .node circle,#mermaid-svg-I86EcudYLT8bzTlH .node ellipse,#mermaid-svg-I86EcudYLT8bzTlH .node polygon,#mermaid-svg-I86EcudYLT8bzTlH .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-I86EcudYLT8bzTlH .rough-node .label text,#mermaid-svg-I86EcudYLT8bzTlH .node .label text,#mermaid-svg-I86EcudYLT8bzTlH .image-shape .label,#mermaid-svg-I86EcudYLT8bzTlH .icon-shape .label{text-anchor:middle;}#mermaid-svg-I86EcudYLT8bzTlH .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-I86EcudYLT8bzTlH .rough-node .label,#mermaid-svg-I86EcudYLT8bzTlH .node .label,#mermaid-svg-I86EcudYLT8bzTlH .image-shape .label,#mermaid-svg-I86EcudYLT8bzTlH .icon-shape .label{text-align:center;}#mermaid-svg-I86EcudYLT8bzTlH .node.clickable{cursor:pointer;}#mermaid-svg-I86EcudYLT8bzTlH .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-I86EcudYLT8bzTlH .arrowheadPath{fill:#333333;}#mermaid-svg-I86EcudYLT8bzTlH .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-I86EcudYLT8bzTlH .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-I86EcudYLT8bzTlH .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I86EcudYLT8bzTlH .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-I86EcudYLT8bzTlH .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I86EcudYLT8bzTlH .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-I86EcudYLT8bzTlH .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-I86EcudYLT8bzTlH .cluster text{fill:#333;}#mermaid-svg-I86EcudYLT8bzTlH .cluster span{color:#333;}#mermaid-svg-I86EcudYLT8bzTlH div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-I86EcudYLT8bzTlH .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-I86EcudYLT8bzTlH rect.text{fill:none;stroke-width:0;}#mermaid-svg-I86EcudYLT8bzTlH .icon-shape,#mermaid-svg-I86EcudYLT8bzTlH .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-I86EcudYLT8bzTlH .icon-shape p,#mermaid-svg-I86EcudYLT8bzTlH .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-I86EcudYLT8bzTlH .icon-shape .label rect,#mermaid-svg-I86EcudYLT8bzTlH .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-I86EcudYLT8bzTlH .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-I86EcudYLT8bzTlH .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-I86EcudYLT8bzTlH :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 清状态 / 最少工作
IRQ_HANDLED
IRQ_WAKE_THREAD
IRQ_NONE
外设拉高 IRQ / MSI
架构入口 handle_domain_irq 等
desc->handle_irq
驱动 primary handler
返回值
结束本轮
唤醒 IRQ 线程
共享中断链下一 handler
thread_fn:可读 I2C / 拷缓冲 / 睡眠
wake_up / complete / queue_work
未清中断源
中断风暴 /proc/interrupts 狂涨

定时路径与可睡眠边界

#mermaid-svg-N4kbfRDSahClGwOt{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-N4kbfRDSahClGwOt .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-N4kbfRDSahClGwOt .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-N4kbfRDSahClGwOt .error-icon{fill:#552222;}#mermaid-svg-N4kbfRDSahClGwOt .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-N4kbfRDSahClGwOt .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-N4kbfRDSahClGwOt .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-N4kbfRDSahClGwOt .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-N4kbfRDSahClGwOt .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-N4kbfRDSahClGwOt .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-N4kbfRDSahClGwOt .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-N4kbfRDSahClGwOt .marker{fill:#333333;stroke:#333333;}#mermaid-svg-N4kbfRDSahClGwOt .marker.cross{stroke:#333333;}#mermaid-svg-N4kbfRDSahClGwOt svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-N4kbfRDSahClGwOt p{margin:0;}#mermaid-svg-N4kbfRDSahClGwOt .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-N4kbfRDSahClGwOt .cluster-label text{fill:#333;}#mermaid-svg-N4kbfRDSahClGwOt .cluster-label span{color:#333;}#mermaid-svg-N4kbfRDSahClGwOt .cluster-label span p{background-color:transparent;}#mermaid-svg-N4kbfRDSahClGwOt .label text,#mermaid-svg-N4kbfRDSahClGwOt span{fill:#333;color:#333;}#mermaid-svg-N4kbfRDSahClGwOt .node rect,#mermaid-svg-N4kbfRDSahClGwOt .node circle,#mermaid-svg-N4kbfRDSahClGwOt .node ellipse,#mermaid-svg-N4kbfRDSahClGwOt .node polygon,#mermaid-svg-N4kbfRDSahClGwOt .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-N4kbfRDSahClGwOt .rough-node .label text,#mermaid-svg-N4kbfRDSahClGwOt .node .label text,#mermaid-svg-N4kbfRDSahClGwOt .image-shape .label,#mermaid-svg-N4kbfRDSahClGwOt .icon-shape .label{text-anchor:middle;}#mermaid-svg-N4kbfRDSahClGwOt .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-N4kbfRDSahClGwOt .rough-node .label,#mermaid-svg-N4kbfRDSahClGwOt .node .label,#mermaid-svg-N4kbfRDSahClGwOt .image-shape .label,#mermaid-svg-N4kbfRDSahClGwOt .icon-shape .label{text-align:center;}#mermaid-svg-N4kbfRDSahClGwOt .node.clickable{cursor:pointer;}#mermaid-svg-N4kbfRDSahClGwOt .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-N4kbfRDSahClGwOt .arrowheadPath{fill:#333333;}#mermaid-svg-N4kbfRDSahClGwOt .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-N4kbfRDSahClGwOt .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-N4kbfRDSahClGwOt .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4kbfRDSahClGwOt .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-N4kbfRDSahClGwOt .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4kbfRDSahClGwOt .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-N4kbfRDSahClGwOt .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-N4kbfRDSahClGwOt .cluster text{fill:#333;}#mermaid-svg-N4kbfRDSahClGwOt .cluster span{color:#333;}#mermaid-svg-N4kbfRDSahClGwOt div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-N4kbfRDSahClGwOt .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-N4kbfRDSahClGwOt rect.text{fill:none;stroke-width:0;}#mermaid-svg-N4kbfRDSahClGwOt .icon-shape,#mermaid-svg-N4kbfRDSahClGwOt .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-N4kbfRDSahClGwOt .icon-shape p,#mermaid-svg-N4kbfRDSahClGwOt .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-N4kbfRDSahClGwOt .icon-shape .label rect,#mermaid-svg-N4kbfRDSahClGwOt .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-N4kbfRDSahClGwOt .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-N4kbfRDSahClGwOt .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-N4kbfRDSahClGwOt :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 驱动业务
回调约束
精度选型
需睡眠
timer_list / jiffies
hrtimer / ktime
硬件定时器 / PWM
定时器回调上下文
不可长时间睡眠
queue_work / 专用线程
超时检测 / 延后释放
高频采样 / 精确截止
强实时控制


重点知识

1. 硬中断里只做「最短必要」

硬中断上下文:不可调度、不可睡眠 。允许:读/清中断状态寄存器、屏蔽该源、搬极少量数据、IRQ_WAKE_THREAD。禁止:mutex_lockkmalloc(GFP_KERNEL)、阻塞 I2C/SPI 传输、大段 printk、在共享锁上等待进程上下文。

违反时轻则 BUG/sleeping function called from invalid context,重则系统卡死或随机丢中断。

2. 优先 request_threaded_irq

把「响应」与「处理」拆开:

部分 上下文 典型工作
handler 硬中断 清源、确认本设备、返回 IRQ_WAKE_THREAD
thread_fn IRQ 内核线程 可读总线、拷贝缓冲、唤醒等待队列

handler == NULL 时内核使用默认主 handler,强制线程化------适合慢速外设。共享中断必须 IRQF_SHARED,且 dev_id 非 NULL、申请与 free_irq 指针一致;IRQF_ONESHOT 常与线程化联用,防止线程跑完前硬中断重入未处理完的逻辑。

Top-half 过重时的替代:tasklet(不可睡,渐少推荐)、workqueue、专用 kthread、NAPI(网络)。选型原则:能睡用线程/work;要极短用硬中断+最小清源

3. timer_list vs hrtimer

timer_list hrtimer
基准 jiffies ktime(通常 CLOCK_MONOTONIC)
精度 ~1 tick(常见 1--10ms) 亚毫秒~微秒级(视配置)
适用 超时、延后释放、粗轮询 精确采样、截止时间、周期短任务
回调 不可睡眠 通常不可睡眠

周期 hrtimer:在回调里 hrtimer_forward_now() 并返回 HRTIMER_RESTART。需要睡眠的工作一律 queue_work。用 jiffies 定时器做 100µs 控制是选型错误,应改 hrtimer 或片上硬件定时器/PWM。

4. 配置与观测

bash 复制代码
# 中断是否进驱动
cat /proc/interrupts | grep -i mydrv
# 绑核(多队列/实时场景)
cat /proc/irq/<n>/smp_affinity
echo <mask> > /proc/irq/<n>/smp_affinity

# 路径与延迟
trace-cmd record -e irq:irq_handler_entry -e irq:irq_handler_exit sleep 2
# 关中断过久:kernelshark / ftrace irqsoff

# 定时器(输出大,调试时用)
# cat /proc/timer_list | head
dmesg | grep -iE 'irq|threaded|NONE'

设备树:interrupts / interrupts-extendedinterrupt-parent 配错 → 申请到错误虚号或 probe 失败。平台驱动优先 platform_get_irq / platform_get_irq_byname,不要手写魔数。

Kconfig:CONFIG_GENERIC_IRQ_DEBUGFS、高精度定时 CONFIG_HIGH_RES_TIMERS(多数发行版已开)。

5. 卸载与竞态

推荐顺序:

  1. 禁硬件中断源(写设备寄存器)
  2. disable_irq(如需要)+ free_irq
  3. del_timer_sync / hrtimer_cancel
  4. cancel_work_sync
  5. 再释放私有数据

踩坑 :模块 exitkfree,定时器/IRQ 仍可能触发 → UAF。共享 IRQ 的 dev_id 申请释放不一致 → free_irq 卸错 handler 或失败。

6. 问题速查

现象 优先查
/proc/interrupts 不涨 未 unmask;DT 映射错;请求了错误号;设备未触发
狂涨、系统卡 未清中断源;电平触发配置错
延迟大、实时差 重活在硬中断;线程优先级;关中断过久
定时不准 误用 jiffies;CPU 休眠影响;应 hrtimer/硬件定时
卸载 oops 未 sync 取消定时器/work;未 free_irq

Checklist

  • 能区分 primary handler 与 thread_fn 的约束,并指出 request_threaded_irq 头文件/实现文件
  • 刺激外设后 /proc/interrupts 对应行计数增加
  • 共享中断使用 IRQF_SHAREDdev_id 唯一、申请释放一致
  • 硬中断路径无睡眠、无 GFP_KERNEL 分配
  • 按精度在 timer_list / hrtimer / 硬件定时器间正确选型并验证到期
  • 卸载:停定时器 → free_irq → 再释放设备内存
  • DT/platform_get_irq 取得的号与 /proc/interrupts 一致
  • 需要时用 ftrace/trace-cmd 确认未在硬中断里跑重逻辑

小结

驱动里的中断与定时,本质是 把工作放到对的上下文 :硬中断保响应,线程/work 保可睡眠与可调度,定时器表达未来时刻且回调同样受上下文约束。选对 request_threaded_irqhrtimer,再用 /proc/interrupts 与 ftrace 验证,丢中断、延迟尖刺和「定时不准」大多能落到具体的一层去改。

相关推荐
考虑考虑2 小时前
nginx打印请求日志
运维·后端·nginx
数字补给站3 小时前
Windows 10 + Hyper-V + Terraform + cloud-init:批量产出 3 台 Ubuntu 的实践笔记
运维·ai编程
分支预测失败3 小时前
RISC-V 核间中断 IPI 实战:从 MSIP 寄存器到 IMSIC 消息投递
linux·后端
这个DBA有点耶4 小时前
同样48核配置TPS差1倍?高性价比数据库一体机的“软硬协同”才是分水岭
服务器·数据库·架构
GeW4 小时前
RHCE考试避坑指南"90%考生容易忽略的细节
linux
键盘会跳舞5 小时前
【C++多线程】死锁诊断工具实战:从 Windows 到 Linux 的全链路排查
linux·c++·windows·死锁
拂拉氏6 小时前
【知识讲解】 Linux环境变量的认识与使用
linux·环境变量
zhangrelay6 小时前
答疑-是否软件源问题都必须更换为国内呢-
linux·笔记·学习·ubuntu·ros2
zhangrelay6 小时前
《ROS 机器人程序设计》课程教学大纲-2026- kinetic-lyrical
linux·笔记·学习·ubuntu·机器人