linux中断机制

中断

Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事。如果要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能:

中断(interrupt) 让硬件在需要的时候向内核发出信号(变内核主动为硬件主动)。
中断的好处是响应及时,如果数据量较小,则不会占用太多的CPU事件;缺点是数据量大时,会产生过多中断,而每个中断都要消耗不少的CPU时间,从而导致效率反而不如轮询高。

轮询(polling) 让内核定期对设备的状态进行查询,然后做出相应的处理;
轮询方式与中断方式相反,它更适合处理大量数据,因为每次轮询不需要消耗过多的CPU时间;缺点是即使只接收很少数据或不接收数据时,也要占用CPU时间。

轮询中断结合
NAPI是两者的结合,数据量低时采用中断,数据量高时采用轮询。平时是中断方式,当有数据到达时,会触发中断处理函数执行,中断处理函数关闭中断开始处理。如果此时有数据到达,则没必要再触发中断了,因为中断处理函数中会轮询处理数据,直到没有新数据时才打开中断。

linux中断机制
从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如 8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断请求线。

中断和异常的区别

中断可分为同步(synchronous)中断和异步(asynchronous)中断:

  1. 同步中断是当指令执行时由 CPU 控制单元产生,之所以称为同步,是因为只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间,比如系统调用。

  2. 异步中断是指由其他硬件设备依照 CPU 时钟信号随机产生,即意味着中断能够在指令之间发生,例如键盘中断。

根据 Intel 官方资料,同步中断称为异常(exception),异步中断被称为中断(interrupt)。
中断可分为可屏蔽中断(Maskable interrupt)和非屏蔽中断(Nomaskable interrupt)。
异常可分为故障(fault)、陷阱(trap)、终止(abort)三类。

中断上下文

当执行一个中断处理程序的时候,内核处于中断上下文,与进程没啥关系,没有后备进程,所以不可以睡眠。
中断处理程序打断了其它的代码(甚至可能打断另一中断处理程序,以及软中断),所以必须快速,简洁

中断控制

控制中断是因为需要提供同步,通过禁止中断,可以确保某个中断处理程序不会抢占当前的代码,还可以禁止内核抢占。
同时,还需要保护机制来防止来自其他处理器的并发访问,内核代码一般都需要锁,伴随着禁止本地中断。
其实就是禁止中断提供保护机制,防止来自其他中断处理程序的并发访问。

上半部与下半部的对比

中断处理程序要快! VS 中断处理程序完成的工作量多!
把中断处理切为两个部分,
上半部是接收到中断就立即执行,但是只做有严格时限的工作
能够允许稍后完成的工作会推迟到下半部,在合适的时机,下半部会开中断执行

中断下半部

软中断和tasklet都是运行在中断上下文中,它们与任一进程无关,没有支持的进程完成重新调度。所以软中断和tasklet不能睡眠、不能阻塞,它们的代码中不能含有导致睡眠的动作,如减少信号量、从用户空间拷贝数据或手工分配内存等。

工作队列是Linux 2.6 内核中新增加的一种下半部机制。它与其它几种下半部分机制最大的区别就是它可以把工作推后,交由一个内核线程去执行。内核线程只在内核空间运行,没有自己的用户空间,它和普通进程一样可以被调度,也可以被抢占。该工作队列总是会在进程上下文执行。这样,通过工作队列执行的代码能占尽进程上下文的所有优势,最重要的就是工作队列允许重新调度甚至是睡眠。

因此,如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择tasklet。另外,如果需要获得大量的内存、需要获取信号量或者需要执行阻塞式的I/O操作时,使用工作队列的方式将非常有用。