linux中断如何使用
linux中断如何使用详细介绍
在 Linux 中使用中断需要涉及内核驱动开发,主要包括中断的注册、处理函数的编写以及上下半部的分离处理。以下是核心步骤和方法的详细说明:
一、核心概念
- 中断分类
- 上半部(Top Half):紧急处理(如清除中断标志、记录数据),需快速完成,禁止睡眠,运行在原子上下文。
- 下半部(Bottom Half):耗时操作(如数据处理、用户空间通知),可延迟执行,支持睡眠,运行在进程上下文或软中断上下文。
- 触发类型
- 边沿触发(
IRQF_TRIGGER_RISING/IRQF_TRIGGER_FALLING) - 电平触发(
IRQF_TRIGGER_HIGH/IRQF_TRIGGER_LOW)
- 边沿触发(
二、使用步骤
1.
获取中断号
- 固定中断号:如已知设备的中断号(如通过
dts设备树描述),或使用内核预定义宏(如IRQ_GPIO_N)。 - GPIO 中断:通过
gpio_to_irq(gpio_num)将 GPIO 编号转换为中断号(需先申请 GPIO:gpio_request)。 - 设备树方式:在驱动中解析设备树节点获取中断信息:c
node pdevdevof_nodeirq node
2.
注册中断处理函数
使用 request_irq 函数注册中断,原型如下:
c
irq handler flagsname dev
- 参数说明:
irq:中断号。handler:上半部处理函数(返回值为irqreturn_t,需返回IRQ_HANDLED或IRQ_NONE)。flags:中断触发类型(如IRQF_TRIGGER_RISING)+ 选项(如IRQF_SHARED共享中断)。name:中断名称(可通过cat /proc/interrupts查看)。dev:共享中断时用于标识设备的指针(非共享时可设为NULL)。
示例:注册非共享中断
c
irq dev_idmy_work
IRQ_HANDLED
result irq_num my_irq_handler IRQF_TRIGGER_FALLING
result
KERN_ERR
result
3.
实现下半部
根据需求选择合适的下半部机制:
- Tasklet(轻量级,非睡眠)
cdata
my_tasklet my_tasklet
my_tasklet
data
- 工作队列(支持睡眠)
cmy_work
my_work my_work_handler
my_work
work
- 线程化中断处理函数(Linux 2.6.30+)
通过设置IRQF_ONESHOT或IRQF_SHARED并将处理函数放在内核线程中(需配合request_threaded_irq):
cirq dev_iddev_id
irq_num my_irq_handler my_threaded_handler
flags dev
irq dev_id
IRQ_HANDLED
dev_id
4.
卸载中断
在驱动移除时调用 free_irq:
c
irq_num dev
三、注意事项
- 原子性与并发控制
- 上半部禁止抢占和中断,需避免使用可能睡眠的函数(如
kmalloc、copy_to_user)。 - 下半部若使用 tasklet / 软中断,仍处于原子上下文;工作队列 / 线程化处理函数处于进程上下文,可使用锁(如
mutex)。
- 上半部禁止抢占和中断,需避免使用可能睡眠的函数(如
- 共享中断
- 多个设备共享同一中断号时,需在
request_irq中设置IRQF_SHARED,并通过设备标识dev区分中断源。 - 处理函数中需先检查当前设备是否为中断源(如读取硬件寄存器),若是返回
IRQ_HANDLED,否则IRQ_NONE。
- 多个设备共享同一中断号时,需在
- 设备树配置
在dts中描述中断(示例):
dtsmy_device {interrupts = <GIC_SPI 100 IRQ_TYPE_EDGE_FALLING>; // GIC 中断,SPI 类型,边沿触发
interrupt-parent = <&gic>;
};
- 调试工具
- 查看中断状态:
cat /proc/interrupts - 统计中断次数:通过
proc文件系统或irq_stat数组(需内核配置支持)。
- 查看中断状态:
四、示例代码框架
c
my_work
irq_num
work
irq dev_id
my_work
IRQ_HANDLED
pdev
irq_num pdevdevof_node
irq_num
irq_num
my_work my_work_handler
ret irq_num my_irq_handler IRQF_TRIGGER_FALLING
ret
ret
pdev
irq_num
my_driver
probe my_probe
remove my_remove
driver
name
of_match_table my_of_ids
my_driver
五、总结
- 分离上下半部:确保上半部快速执行,下半部处理复杂逻辑。
- 选择合适的下半部:
- 高性能场景:tasklet / 软中断(不可睡眠)。
- 需睡眠场景:工作队列 / 线程化中断处理函数。
- 错误处理:注册中断时检查返回值,避免资源泄漏。
通过以上步骤,可在 Linux 驱动中正确实现中断处理,平衡实时性与系统性能。