Linux内核中断学习

时间:2023-03-08 16:03:17
Linux内核中断学习

1、内核中断概述

(1)在OS环境下编写中断处理函数与之前在裸机中编写中断处理函数的方式是不一样的,在Linux内核中提供了一套用来管理硬件中断资源的软件体系架构。

(2)在操作系统中,中断号与gpio、内存一样被认为是一种硬件资源,当我们需要使用某一个中断号时必须向操作系统申请中断资源,只有申请成功才能够使用,否则

就不能被自己使用。

2、中断的申请及释放

在Linux内核中提供了专用接口用来申请中断资源,这个接口就是:request_irq

(1)函数原型: request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags, const char *name, void *dev)

(2)参数说明:

irq:  需要申请的中断号,需要注意的是这里的中断号是内核中相关头文件中定义好的,与实际的硬件平台是相对应的,这个是内核移植的时候就做好的,不需要你去关系。

handler:  中断处理函数指针

flags:  用来描述中断属性的标志位,例如:中断的触发方式、本中断号是不是共享中断......更加详细的请参考 include\linux\interrupt.h 文件

name:  该中断的名字

dev:  对于这个参数,很多人说是用于共享中断(也就是多个中断源共有一个中断号),但是我觉得不是这么回事,当如果申请中断号的时候多个中断号是绑定在一个中断处理函数

中的时候,我们其实是可以通过dev传参来确定到底是那个中断号发生了中断事件,从而执行这个中断相关的代码;同样当我们在释放中断的时候也是要传入相同的参数。

内核中释放中断的函数: free_irq

(1)函数原型:  void free_irq(unsigned int irq, void *dev_id)

(2)参数说明:

irq:  需要释放的中断号

dev_id:  这个参数就是request_irq中的dev

 

3、中断上下文

(1)第一首先需要明确的是:中断为什么需要分为上下文?

一般来说,中断处理函数代码执行的时间需要非常短,因为中断是不会参与内核调度,那么也就意味着进入中断必须要一直持有CPU不能释放,直到中断处理函数执行完毕才可以释放

CPU,如果中断处理函数执行时间过长必然会影响系统的实时性。

(2)那么一个很现实的问题就是:如果我们中断处理函数中必须要做很多的事情,没办法将时间缩短下来,该怎么办?

这就得用到今天讲的中断上下文。在Linux内核中将中断处理函数分文上下半部,也就是中断上下文。将中断处理函数分为上下半部之后,就是将其分为两个部分

(3)中断处理的注意点

1): 中断上下文,不能和用户空间数据交互

因为有可能导致休眠,而在中断中交出CPU之后是不能够在断点处获取到CPU继续执行下面的代码的,因为中断函数是不参与调度系统的,所以这样做的话就会导致中断函数

并不能够完美的执行。

2): 不能交出CPU(不能休眠、不能schedule交出CPU)

3): ISR运行时间尽可能短,越长则系统响应特性越差

4、中断下半部的两种解决方案

(1)下半部的处理策略1:tasklet(小任务)

初始化一个tasklet: DECLARE_TASKLET(name, func, data)      //  定义一个名为name的tasklet任务,并将其初始化,func表示下半部绑定的函数,data表示传给func的参数

DECLARE_TASKLET_DISABLED(name, func, data)  // 与上面一样,但是任务默认是关闭的

如何在中断的上半部开启tasklet任务:

tasklet_schedule(struct tasklet_struct *t)   //  我们只需要在上半部函数中调用这个函数即可,t就是上面的name的指针

使能/禁止下半部:

tasklet_disable(struct tasklet_struct *t)     // 禁止执行中断下半部

tasklet_enable(struct tasklet_struct *t)      //  使能

(2)下半部的处理策略2:workqueue(工作队列)

初始化一个workqueue:  struct work_struct irq_queue;    INIT_WORK(&irq_queue, do_irq_queuework);   // 先定义一个变量再初始化,do_irq_queuework表示绑定的函数

DECLARE_WORK(n, f)      //  定义+初始化

如何在中断上半部开启workqueue工作队列:

schedule_work(struct work_struct *work);

5、中断上下半部处理原则

(1)必须立即进行紧急处理的极少量任务放入在中断的顶半部(上半部)中,此时屏蔽了与自己同类型的中断,由于任务量少,所以可以迅速不受打扰地处理完紧急任务。

(2)需要较少时间的中等数量的急迫任务放在tasklet中。此时不会屏蔽任何中断(包括与自己的顶半部同类型的中断),所以不影响顶半部对紧急事务的处理;同时又不会进

行用户进程调度,从而保证了自己急迫任务得以迅速完成。

(3)需要较多时间且并不急迫(允许被操作系统剥夺运行权)的大量任务放在workqueue中。此时操作系统会尽量快速处理完这个任务,但如果任务量太大,期间操作系统也会

有机会调度别的用户进程运行,从而保证不会因为这个任务需要运行时间将其它用户进程无法进行。

(4)可能引起睡眠的任务放在workqueue中。因为在workqueue中睡眠是安全的。在需要获得大量的内存时、在需要获取信号量时,在需要执行阻塞式的I/O操作时,用workqueue

很合适。

/**************************************************************************************************************************/

未完待续。。。。。。。。。。。。。。。