目录
- 什么是硬件中断?用公司紧急会议类比一秒懂
- 中断号:会议的唯一ID
- 中断注册与注销完整流程(核心API+代码示例)
- 中断上下文:会议室里的特殊规则(和你日常工作对比)
- 中断上下文绝对禁止操作清单(用会议场景逐条解释)
- 核心总结+面试必背考点
1. 什么是硬件中断?用公司紧急会议类比一秒懂
如果你是一名应用开发者,那么你写的代码就像你在公司的日常工作:按计划写代码、改bug、回邮件,一件事做完再做下一件,可以随时停下来喝口水、去厕所(睡眠/阻塞)。
而硬件中断,就是公司突然发来的紧急会议通知——不管你手头在做什么,必须立刻放下,去会议室开会,会议结束后再回来继续之前的工作。
完美对应关系表(一个都不差)
| Linux中断概念 | 公司紧急会议对应 | 核心含义 |
|---|---|---|
| CPU | 你(正在工作的员工) | 执行所有任务的主体 |
| 硬件设备 | 公司各个部门(客服、运维、老板) | 产生紧急事件的源头 |
| 中断信号 | 钉钉/企业微信强提醒弹窗+电话轰炸 | 通知CPU有紧急事件发生 |
| 中断控制器 | 行政前台 | 接收所有紧急通知,按优先级排序后通知你 |
| 中断号 | 会议ID | 区分不同紧急会议的唯一标识 |
| 中断处理函数 | 你参加紧急会议的过程 | 处理紧急事件的代码 |
| 进程上下文 | 你的日常工作(写代码、写文档) | 普通代码的运行环境,可以睡眠、可以被打断 |
| 中断上下文 | 会议室 | 处理紧急事件的特殊环境,有严格的规则 |
| 下半部(tasklet/工作队列) | 会议结束后的后续工作(写纪要、分配任务) | 非紧急的耗时操作,会后再做 |
完整工作流程(和你上班一模一样)
- 硬件触发中断:客服接到客户投诉,系统崩了 → 发紧急会议通知
- 中断控制器转发:前台收到通知,判断是最高优先级的线上故障 → 立刻打电话给你
- CPU暂停当前工作:你正在写代码,接到电话 → 立刻保存当前代码进度,合上电脑
- 执行中断处理函数:你跑到会议室,参加10分钟的紧急故障排查会
- 恢复之前的工作:会议结束 → 你回到工位,打开电脑,继续写刚才的代码
和应用信号(signal)的本质区别
很多应用开发者会把中断和信号搞混,用这个类比一眼就能看出区别:
- 应用信号:同事给你发了一条微信消息,你可以等手头这行代码写完再看
- 硬件中断:老板直接冲进你的工位,把你拉去会议室,必须立刻走
2. 中断号:会议的唯一ID
就像每个会议都有唯一的会议ID一样,每个硬件中断也有唯一的中断号(IRQ number)。
2.1 中断号的作用
- 前台(中断控制器)通过会议ID知道是哪个部门的会议
- 你(CPU)通过会议ID知道要去哪个会议室、参加哪个会议
- 内核通过中断号找到对应的中断处理函数执行
2.2 驱动中获取中断号的正确方式
绝对不要硬编码中断号!就像你不会把会议ID写死在代码里一样,现代驱动必须从设备树中动态获取:
// 在platform驱动的probe函数中获取中断号intirq=platform_get_irq(pdev,0);if(irq<0){dev_err(&pdev->dev,"获取会议ID失败,irq=%d\n",irq);returnirq;}dev_info(&pdev->dev,"获取到会议ID:%d\n",irq);对应的设备树节点:
mydevice@12340000 { compatible = "mytest,dev"; reg = <0x12340000 0x1000>; interrupts = <5 IRQ_TYPE_LEVEL_HIGH>; // 会议ID=5,高电平触发 status = "okay"; };3. 中断注册与注销完整流程(核心API+代码示例)
中断注册就像你在公司系统里报名参加紧急会议,告诉前台:“以后会议ID为X的会议,都通知我来参加”。
3.1 核心API:request_irq(报名参加会议)
intrequest_irq(unsignedintirq,irq_handler_thandler,unsignedlongflags,constchar*name,void*dev_id);参数对应会议场景
| 参数 | 会议场景含义 | 常用值 |
|---|---|---|
irq | 会议ID | 从platform_get_irq()获取 |
handler | 你参加会议的过程 | 自己实现的中断处理函数 |
flags | 会议规则 | 触发方式+是否允许共享会议室 |
name | 会议名称 | 驱动名称,会显示在公司会议系统里 |
dev_id | 你带的笔记本电脑 | 传递给会议的个人物品,会后还要用 |
常用flags标志
| 标志 | 会议场景含义 |
|---|---|
IRQF_TRIGGER_RISING | 门铃响一声就开会(上升沿触发) |
IRQF_TRIGGER_HIGH | 门铃一直响,直到你开门(高电平触发) |
IRQF_SHARED | 多个部门共用一个会议室(共享中断) |
3.2 核心API:free_irq(取消会议报名)
voidfree_irq(unsignedintirq,void*dev_id);- 对应场景:你离职了,以后这个会议再也不用通知你了
- 注意:
dev_id必须和报名时带的笔记本电脑完全一致,否则前台不让你取消
3.3 完整的中断注册与注销代码示例
#include<linux/interrupt.h>// 设备私有数据结构体(对应你带的笔记本电脑)structmy_dev{intirq;// 会议IDvoid__iomem*reg;// 会议室地址// ... 其他资源};// 中断处理函数(你参加会议的过程)staticirqreturn_tmy_irq_handler(intirq,void*dev_id){structmy_dev*dev=dev_id;// 1. 先按门铃停止(必须做!否则门铃会一直响)writel(0x1,dev->reg+0x04);// 2. 只做最紧急的事:记录故障现象dev->fault_code=readl(dev->reg+0x08);// 3. 通知运维同事过来处理wake_up_interruptible(&dev->wait_queue);// 4. 耗时的故障修复工作,会后再做tasklet_schedule(&dev->repair_tasklet);dev_info((structdevice*)dev_id,"故障排查会议结束\n");returnIRQ_HANDLED;// 会议是我的,我已经处理完了}staticintmy_probe(structplatform_device*pdev){structmy_dev*dev;intret;// ... 其他初始化代码// 1. 获取会议IDdev->irq=platform_get_irq(pdev,0);if(dev->irq<0){returndev->irq;}// 2. 报名参加会议ret=request_irq(dev->irq,my_irq_handler,IRQF_TRIGGER_RISING,// 门铃响一声就开会pdev->name,// 会议名称dev);// 带我的笔记本电脑if(ret){dev_err(&pdev->dev,"报名会议失败,ret=%d\n",ret);returnret;}dev_info(&pdev->dev,"成功报名会议,会议ID:%d\n",dev->irq);return0;}staticintmy_remove(structplatform_device*pdev){structmy_dev*dev=dev_get_drvdata(&pdev->dev);// 取消会议报名free_irq(dev->irq,dev);// ... 其他清理代码return0;}4. 中断上下文:会议室里的特殊规则(和你日常工作对比)
你在工位上(进程上下文)可以做任何事:喝水、去厕所、刷手机、等外卖。但一旦进入会议室(中断上下文),就必须遵守严格的规则,否则会影响整个公司的运转。
4.1 会议室的3条铁律(中断上下文的核心特性)
- 会议必须短:紧急会议只能开5-10分钟,不能开3小时,否则所有人的工作都耽误了
- 会议期间不能离开:会议没结束你不能走,不能中途去做其他事
- 会议室里没有你的日常用品:会议室里没有你的电脑、文件、水杯,你只能处理和会议相关的事
4.2 和日常工作(进程上下文)的核心区别
| 对比项 | 日常工作(进程上下文) | 紧急会议(中断上下文) |
|---|---|---|
| 能否离开工位(睡眠) | ✅ 可以(去厕所、喝水) | ❌ 绝对不能,会议没结束不能走 |
| 能否被打断(调度) | ✅ 可以(老板叫你、同事找你) | ❌ 不能,你是会议的主角,没人能打断你 |
| 时间限制 | 无严格限制 | 必须极短(微秒级,5-10分钟) |
| 可用物品(API) | 所有办公用品 | 只有会议室里的东西(原子性、非睡眠API) |
| 能否处理日常工作(访问用户态内存) | ✅ 可以 | ❌ 不能,会议室里没有你的电脑和文件 |
4.3 为什么中断上下文绝对不能睡眠?
用会议场景解释:
紧急会议开到一半,你突然睡着了。
所有参会的人都在等你醒来,故障没人处理,线上服务一直崩着。
老板、客服、客户都在催,但你就是不醒。
最后整个公司都停摆了,只能强制重启(断电)。
这就是为什么中断上下文睡眠会导致整个系统挂起的原因。
5. 中断上下文绝对禁止操作清单(用会议场景逐条解释)
这是驱动开发中最致命、最容易犯的错误,也是面试100%会问的考点。每一条都用会议场景解释,看完永远不会忘。
❌ 1. 绝对不能调用任何可能睡眠的函数
错误示例:
msleep(100);// 会议开到一半睡着了wait_event_interruptible(wq,condition);// 等外卖送到再继续开会mutex_lock(&lock);// 等别人把会议室钥匙给你会议场景解释:
会议开到一半你睡着了,或者等外卖、等别人拿钥匙,所有人都在等你,故障没人处理,公司直接停摆。
正确替代:
- 睡眠操作:移到会后(工作队列)做
- 同步操作:用自旋锁(原地等,不睡觉)代替互斥锁
❌ 2. 绝对不能使用GFP_KERNEL标志分配内存
错误示例:
char*buf=kmalloc(1024,GFP_KERNEL);// 错误!正确写法:
char*buf=kmalloc(1024,GFP_ATOMIC);// 正确,从会议室备用物资里拿会议场景解释:GFP_KERNEL相当于"等仓库开门了再给你拿纸",但会议正在进行中,不能等。GFP_ATOMIC相当于"直接从会议室的备用纸抽里拿",立刻就能拿到,不用等。
❌ 3. 绝对不能访问用户态内存
错误示例:
copy_to_user(user_buf,kernel_buf,len);// 错误!会议场景解释:
用户态内存就是你工位上的电脑和文件,会议室里没有这些东西。你不能在会议室里处理你工位上的日常工作。
正确替代:
在回到工位后(进程上下文的read/write函数)再处理用户态内存,会议上只把数据记在会议室的白板上(内核缓冲区)。
❌ 4. 绝对不能执行任何耗时操作
错误示例:
// 错误!开3小时的会for(inti=0;i<1000000;i++){do_something();}// 错误!在会议上写30页会议纪要for(inti=0;i<100;i++){printk(KERN_INFO"会议记录 %d\n",i);}会议场景解释:
紧急会议只能开5-10分钟,用来快速定位问题。耗时的故障修复、写会议纪要等工作,应该在会议结束后(下半部)再做。
❌ 5. 绝对不能使用互斥锁和信号量
错误示例:
mutex_lock(&lock);// 错误!正确写法:
spin_lock(&lock);// 正确,原地等别人说完,不睡觉会议场景解释:
互斥锁相当于"等别人用完会议室你再进",但你已经在会议室里了,不能等。自旋锁相当于"别人正在说话,你原地等他说完你再说",不用离开会议室。
❌ 6. 绝对不能在会议中辞职(卸载模块)
错误示例:
module_put(THIS_MODULE);// 错误!会议场景解释:
会议开到一半你突然辞职走人了,剩下的人没人主持,故障没人处理,公司直接完蛋。
6. 核心总结+面试必背考点
核心总结(用会议类比一句话记住)
硬件中断就是公司的紧急会议:必须立刻参加、必须短、不能睡觉、不能处理日常工作,所有耗时的事都会后再做。
面试必背考点
用一句话解释什么是硬件中断?
硬件中断是硬件设备异步通知CPU有事件发生的机制,CPU会立即暂停当前工作,执行中断处理函数,处理完成后再恢复之前的工作。
中断上下文为什么绝对不能睡眠?
中断上下文不关联任何进程,一旦睡眠,内核无法调度其他进程运行,整个系统会直接挂起。
中断处理函数中为什么不能执行耗时操作?
中断处理函数执行时,整个系统的其他所有代码都被暂停,耗时过长会导致系统响应变慢、外设丢包、卡顿甚至看门狗复位。
中断上下文绝对不能做哪些操作?
不能睡眠、不能用GFP_KERNEL分配内存、不能访问用户态内存、不能执行耗时操作、不能用互斥锁和信号量、不能卸载模块。
互斥锁和自旋锁的区别是什么?
互斥锁等待时会睡眠,只能用在进程上下文;自旋锁等待时会原地自旋,不会睡眠,可以用在中断上下文。