在 Linux 内核中,中断请求 (IRQ) 对于管理硬件中断至关重要,它允许硬件设备向处理器发出信号,表示它们需要注意。IRQ 的处理对于响应迅速且高效的系统性能至关重要。以下是 Linux 内核 5.4 中 IRQ 的详细说明,涵盖了它们的注册、处理和相关数据结构:
1. IRQ 基础知识
IRQ是发送给处理器的硬件信号,用于中断当前代码执行并处理特定事件,例如来自键盘的输入或到达的网络数据包。
中断控制器是一种对这些中断进行优先排序并将其分派给 CPU 的硬件。
2. IRQ 数据结构
Linux Kernel 5.4 中用于处理 IRQ 的关键数据结构包括:
- struct irq_desc:代表每个中断的描述符,包含 IRQ 编号、处理程序、状态和亲和性等信息。
- struct irq_chip:代表中断控制器硬件操作。
- struct irqaction:表示发生中断时要采取的操作,包括处理函数和标志。
3. IRQ 注册
设备使用类似 的函数注册其中断处理程序request_irq()
。以下是 IRQ 处理程序注册的简化视图:
arduino
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)
- irq:中断号。
- handler:中断发生时调用的函数。
- flags:控制中断行为的标志。
- name:中断的名称。
- dev:指向设备特定数据的指针。
例子:
arduino
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
// Handle the interrupt
return IRQ_HANDLED;
}
int my_device_init(void)
{
int irq = 10; // Example IRQ number
int ret;
ret = request_irq(irq, my_irq_handler, IRQF_SHARED, "my_device", &my_device_data);
if (ret) {
pr_err("Failed to request IRQ %d\n", irq);
return ret;
}
return 0;
}
4. IRQ 处理
当发生中断时,处理器停止执行当前指令并跳转到中断处理程序。流程如下:
- 中断发生:硬件设备发出中断信号。
- 中断控制器:控制器确定优先级并将中断发送给 CPU。
- 中断处理 :
- CPU 确认中断并执行为特定 IRQ 注册的中断处理程序。
- 处理程序执行后,控制权将返回给被中断的任务。
5. 上半部分和下半部分
Linux 将中断处理分为两部分:
- 上半部:初始中断处理程序,它快速执行以确认中断并可能调度下半部。
- 下半部:将大部分处理推迟到以后进行。类型包括软中断、小任务和工作队列。
6. IRQ 亲和性
IRQ 亲和性决定了哪些 CPU 处理特定中断。可以使用proc
文件系统来控制:
bash
echo 2 > /proc/irq/10/smp_affinity # Set IRQ 10 to CPU 2
7. IRQ 管理功能
- free_irq():释放先前分配的 IRQ。
- disable_irq():禁用给定的 IRQ 线。
- enable_irq():启用给定的 IRQ 线路。
8.示例演练
下面是一个简化的示例来说明 IRQ 注册和处理:
arduino
#include <linux/module.h>
#include <linux/interrupt.h>
#define MY_IRQ 10
static irqreturn_t my_irq_handler(int irq, void *dev_id)
{
pr_info("IRQ %d: Interrupt occurred\n", irq);
// Handle the interrupt
return IRQ_HANDLED;
}
static int __init my_module_init(void)
{
int ret;
ret = request_irq(MY_IRQ, my_irq_handler, IRQF_SHARED, "my_device", &my_device_data);
if (ret) {
pr_err("Failed to request IRQ %d\n", MY_IRQ);
return ret;
}
pr_info("Module loaded, IRQ %d registered\n", MY_IRQ);
return 0;
}
static void __exit my_module_exit(void)
{
free_irq(MY_IRQ, &my_device_data);
pr_info("Module unloaded, IRQ %d freed\n", MY_IRQ);
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Author");
MODULE_DESCRIPTION("IRQ handling example");
9.中断上下文和同步
中断处理程序在中断上下文中运行,这意味着它们不能阻塞、休眠或执行可能阻塞的操作。通常需要同步才能在中断处理程序和内核的其他部分之间安全地共享数据。自旋锁通常用于此目的。
10. 总结
- 注册 :用于
request_irq()
注册中断处理程序。 - 处理:上半部分快速处理,将较长的任务推迟到下半部分。
- 亲和性(Affinity):控制哪个 CPU 处理中断。
- 同步:确保中断处理程序和其他上下文之间的安全数据访问。
理解和管理 IRQ 对于开发高效且响应迅速的内核模块和设备驱动程序至关重要。Linux Kernel 5.4 继续使用这些基础概念,并进行了增强和优化,以实现更好的性能和可扩展性。