1. 注册线程化中断处理函数
devmem_request_threaded_irq
是 Linux 内核中的一个函数,用于请求并注册一个线程化的中断处理程序。这个函数允许开发者注册一个中断处理函数,这个函数会在中断发生时被调用,从而实现相应的中断处理逻辑。它通过 devm
机制分配资源,这意味着在设备的生命周期结束时,资源会自动释放,无需手动进行释放,这有助于避免资源泄漏问题。
函数原型如下:
int devm_request_threaded_irq(struct device *dev, unsigned int irq,irq_handler_t handler, irq_handler_t thread_fn,unsigned long irqflags, const char *devname,void *dev_id);static inline int __must_check
devm_request_irq(struct device *dev, unsigned int irq, irq_handler_t handler,unsigned long irqflags, const char *devname, void *dev_id)
{return devm_request_threaded_irq(dev, irq, handler, NULL, irqflags,devname, dev_id);
}
参数说明:
dev
: 指向表示设备的struct device
结构体的指针。irq
: 中断号,表示要请求和注册的中断线路。handler
: 中断处理程序的主处理函数,它在中断发生时执行。thread_fn
: 线程处理函数,它在中断上下文之外以线程化的方式执行,通常用于执行一些比较耗时的操作。irqflags
: 中断标志,用于指定中断处理的属性和行为。devname
: 设备名称,用于标识中断请求的设备。dev_id
: 设备标识符,可以是设备结构体或其他设备特定的标识符。
使用 devmem_request_threaded_irq
可以提高中断处理的效率,特别是在处理高频率中断时,因为它允许将耗时的中断处理逻辑移到一个线程中执行,而不是在中断上下文中直接处理,这样可以减少中断处理时间,提高系统响应速度。
2.为什么要注册一个线程化的中断处理函数?
注册一个线程化的中断处理函数主要是为了提高系统的实时性能和响应性,特别是在实时或嵌入式系统中。线程化中断处理函数与普通中断处理函数的主要区别在于它们的执行环境和处理方式。
-
执行环境:普通中断处理函数(也称为中断服务例程,ISR)在中断上下文中执行,这个上下文是不可抢占的,意味着一旦中断发生,CPU会立即暂停当前执行的任务去处理中断,直到ISR执行完毕。这种方式虽然响应速度快,但如果ISR执行时间过长,会阻塞其他中断和任务的执行,影响系统实时性。
-
处理方式:线程化的中断处理函数将中断处理分为两部分:快速的硬件中断处理程序(Fast hardware ISR)和线程中断处理程序(Threaded IRQ handler)。硬件ISR负责快速响应中断,执行必要的硬件操作,如确认中断源、禁用中断源等,然后唤醒线程化的中断处理函数。线程化的中断处理函数在独立的线程上下文中运行,可以被抢占,允许其他高优先级任务在必要时抢占执行,从而减少对系统实时性的影响。
-
优势:线程化中断处理程序的主要优势在于它们可以被抢占,允许系统在必要时调度其他任务,从而提高系统的响应性和实时性能。此外,线程化中断处理程序可以在线程上下文中使用内核API,如互斥锁等,简化了驱动程序的设计和同步。
-
适用场景:线程化中断处理程序适用于那些需要快速响应但处理过程可能较长的中断,如网络数据包处理。通过将耗时的处理逻辑移到线程中,可以减少中断上下文的执行时间,降低对系统实时性的影响。
-
实现方式:在Linux内核中,可以通过
request_threaded_irq
函数来注册线程化的中断处理函数。这个函数允许指定一个硬件ISR和一个线程化的中断处理函数,硬件ISR在中断发生时快速响应,而线程化的中断处理函数在独立的线程中执行。
总的来说,线程化中断处理函数通过将中断处理逻辑分散到不同的执行环境,有助于提高系统的实时性能和响应性,特别是在需要处理大量或复杂中断任务的场景中。
3. request_irq 申请中断
request_irq
是 Linux 内核中用于注册中断处理程序的函数。它允许设备驱动程序请求一个中断号(IRQ),并提供一个中断处理函数(handler),当指定的中断发生时,内核将调用这个处理函数。这个函数通常用于需要处理硬件中断的设备驱动程序中。
函数原型如下:
int request_irq(unsigned int irq, irq_handler_t handler, unsigned long irqflags, const char *devname, void *dev_id);
参数说明:
irq
: 指定要分配的中断号。handler
: 中断处理函数指针,当中断发生时,系统将调用这个函数。irqflags
: 中断处理的属性,可以指定快速中断、中断共享等属性。devname
: 设备名称,用于标识中断请求的设备。dev_id
: 设备标识符,可以是设备结构体或其他设备特定的标识符,用于在共享中断中区分不同的设备。
使用 request_irq
时,如果返回值是0,则表示申请成功;如果申请不成功,则返回的值非零,一般为负数,可能的取值包括 -EINVAL
表示中断号无效或处理函数指针为NULL,-EBUSY
表示中断已经被占用且不能共享。
此外,request_irq
函数在 2.4 内核和 2.6 内核中都使用,但在 2.6 内核中,需要包含的头文件是 #include <linux/interrupt.h>
。在 2.4 内核中,需要包含的头文件是 #include <linux/sched.h>
。
request_irq
函数的作用是申请使用 IRQ 并注册中断处理程序。当 CPU 接收到中断请求后,可以根据中断号通过 irq_desc[]
找到该设备的中断服务程序。
在共享中断的情况下,dev_id
参数是必需的,并且 dev_id
的值必须唯一。这有助于在共享中断线上区分不同的设备。
request_irq()函数可能会导致睡眠,因此不能在中断上下文或者其他禁止睡眠的代码段中使用 。
与request_irq() 函数相比request_threaded_irq() 函数仅多了一个入参thread_fn ,thread_fn()为在线程中运行的函数。
Appendix.
irqflags:中断标志,可以在文件 include/linux/interrupt.h 里面查看所有的中断标志,这里我们介绍几个常用的中断标志。
标志 | 描述 |
---|---|
IRQF_SHARED | 多个设备共享一个中断线,共享的所有中断都必须指定此标志。如果使用共享中断的话,request_irq 函数的 dev 参数就是唯一区分他们的标志 |
IRQF_ONESHOT | 单次中断,中断执行一次就结束 |
IRQF_TRIGGER_NONE | 无触发 |
IRQF_TRIGGER_RISING | 上升沿触发 |
IRQF_TRIGGER_FALLING | 下降沿触发 |
IRQF_TRIGGER_HIGH | 高电平触发 |
IRQF_TRIGGER_LOW | 低电平触发 |
返回值: | 0 中断申请成功,其他负值 中断申请失败,如果返回-EBUSY 的话表示中断已经被申请了。 |