【Linux内核】内核中的中断管理
Linux内核支持众多处理器架构,它对各种架构都做了相当的适配,Linux内核中的中断管理可以分为以下四层。
- 硬件层,比如CPU和中断控制器的连接
- 处理器架构管理层,比如CPU中断异常处理
- 中断控制器管理层,比如IRQ中断异常的处理
- Linux内核通用中断处理器层,比如中断注册和中断处理
硬件中断号和Linux中断号的映射
软中断和tasklet
中断管理中有一条很重要的原则:硬件中断处理程序应该执行得越快越好,这是因为硬件中断处理程序会打断其他重要代码的执行,为了避免打断时间过长,硬件中断处理应该尽快执行完毕。话有一个原因是中断处理程序会在关中断的情况下执行,所谓关中断指的是关闭本地CPU的所有中断响应,这也要求中断最好尽快执行完毕。
为了应对中断执行时间应该尽可能短的要求,Linux中断管理引入了一个很重要的设计理念——上下半部机制。在Linux内核中,上下半部机制是一种用于处理中断和延迟任务的机制,它将中断处理分为两个部分:上半部(也称为中断服务例程,ISR,或者硬中断)和下半部(也称为延迟任务处理,或者称为软中断)。这种机制的设计目的是为了提高系统的效率和响应速度。
上半部是中断处理的第一部分,它在硬件中断发生时被调用。上半部的执行具有较高的优先级,它会在中断发生时立即打断当前的进程执行,转而执行中断服务例程。上半部的执行时间通常很短,因为它只完成一些紧急且必要的操作,比如读取硬件寄存器的状态、关闭中断源等。上半部的执行环境是内核空间,它不能睡眠,也不能使用可能引起睡眠的函数。
下半部是中断处理的第二部分,它在上半部执行完毕后被调度执行。与上半部不同,下半部的执行优先级较低,它可以执行一些相对不那么紧急但可能需要更多时间来完成的任务,比如数据处理、内存分配等。下半部的执行环境也是内核空间,但它可以睡眠,可以使用可能引起睡眠的函数。
软中断
软中断是Linux内核很早引入的机制,最早可以追到Linux2.3开发期间。软中断是预留给系统中对时间要求最严格和最重要的下半部使用的,而且目前驱动中只有块设备和网络子系统使用了软中断。系统静态定义了若干软中断类型,并且Linux内核开发者不希望用户再扩充新的软中断类型,如有需要,建议使用tasklet
机制。
其中索引号越小,软中断的优先级越高,从而在一轮软中断处理中得到优先执行。
- HI_SOFTIRQ,优先级为0,是优先级最高的软中断类型。
- TIMER_SOFTIRQ,优先级为1,是用于定时器的软中断。
- NET_TX_SOFTIRQ,优先级为2,是用于发送网络数据包的软中断。
- NETRX_SOFTIRQ,优先级为3,是用于接收网络数据包的软中断。
- BLOCKSOFTIRQ和BLOCKIOPOLL_SOFTIRQ,优先级分别是4和5,是用于块设备的软中断。
- TASKLET_SOFTIRQ,优先级为6,是专门为tasklet机制准备的软中断。
- SCHED_SOFTIRQ,优先级为7,用于进程调度以及负载均衡。
- HRTIMER_SOFTIRQ,优先级为8,是一种高精度定时器。
- RCU_SOFTIRQ,优先级为9,是专门为RCU服务的软中断
tasklet
tasklet
是利用软中断实现的一种下半部机制,本质是软中断的变种,运行在软中断的上下文中,其数据结构如下:
[include/linux/interrupt.h]struct tasklet_struct
{struct tasklet_struct *next;unsigned long state;atomic_t count;void (*func)(unsigned long);unsigned long data;
};
其中next指向下一个tasklet节点;state用于表示tasklet是正在运行还是准备中;count用于规定tasklet是否处于激活状态,如果不是0则表示不允许执行;func用于指向tasklet的处理程序,而data就是传递给tasklet的参数。
每个CPU维护两个tasklet链表,一个是普通优先级的task_vec
,另外一个是高优先级的tasklet_hi_vec
。他们都是Pre-CPU变量。如果要在驱动中使用tasklet机制,则可以使用DECLARE_TASKLET
静态声明或者tasklet_init()
动态初始化一个tasklet,调度则使用tasklet_schedule
函数
工作队列机制
工作队列机制是除了软中断和tasklet以外最常用的下半部机制之一。工作队列的基本原理是把work(需要推迟执行的函数)交由一个内核线程执行,并且总是在进程上下文中执行。工作队列的优点是利用进程上下文来执行中断下半部操作,因此工作队列允许重新调度和睡眠,是异步执行的进程上下文,另外还能解决因软中断和tasklet执行时间过长而导致系统实时性下降等问题。当驱动程序或内核子系统在进程上下文中有异步执行的工作任务时,可以使用工作项来描述工作任务。把工作项添加到一个队列中,然后由一个内核线程执行工作任务的回调函数。
当驱动程序或者内核子系统在进程上下文中有异步执行的工作任务的时候,可以使用工作项来描述工作任务,然后把这个工作项添加到一个工作队列中,然后使用一个内核线程执行工作任务的回调函数,这个内核线程被称为worker。
工作队列和tasklet的区别是,tasklet主要运行在中断的上下文中,属于中断处理的下半部,因此也是不能睡眠不能阻塞的,而工作队列则运行在进程上下文中,属于内核线程,它可以睡眠也可以被阻塞。这个根本区别也带来了一些其他特性上的不同,比如说tasklet适合快速响应、不能被阻塞的场景,而工作队列适合执行所需时间较长并且可能被阻塞的任务