第6章 中断与中断处理

概述


管理系统中存在的各种设备是内核的主要职责,内核一般可以通过两种方式来履行这个职责:

轮询:内核以一定的周期访问设备,查询设备的状态并进行处理。

中断:设备在需要时通知内核,内核收到设备的请求后再做出相应处理。


使用轮询方式时,内核对设备的访问按照一定频率周期地进行,因而常常会出现设备的请求不能得到及时的处理,或者在内核一次又一次的访问中没有

发现设备的任何请求,进而消费了大量的CPU资源的情况。

而使用中断方式则不同,内核成为被动的一方,只有在收到设备的中断请求时,才中断当前正在进行的工作去进行中断处理。


中断分类


中断可分为同步中断和异步中断。

同步中断由CPU本身产生,又称为内部中断。这里的同步是指中断请求信号与代码指令之间的同步执行,即只有在一条指令执行完毕后,而不是执行期

间,CPU才会发出中断。

异步中断有其他硬件设备产生,又称为外部中断。与同步中断相反,异步中断可于任何时间产生,包括指令执行期间。

通常将同步中断称为异常,将异步中断称为中断,我们也将采用这种说法。

异常产生于CPU在指令执行期间检测到异常或非法的条件时,与当前正在执行的指令有直接的联系。而中断通常用于指示设备的特定操作已经完成,与

当前正执行的指令之间并没有直接联系。

异常包括故障、陷阱和中止。故障是指在引起异常的指令之前,把该异常情况通知给系统的一种异常。故障处理结束后,返回到引起故障的指令并执行。

陷阱是指在引起异常的指令之后,把该异常情况通知给系统的一种异常。在转入相应的处理程序进行陷阱处理时,引起陷阱的指令应该已经正常完成,

因此陷阱处理结束后,将返回到引起陷阱的指令的下一条指令并执行。

中止是在系统出现严重情况时,通知系统的一种异常。引起中止的指令是无法确定的,产生中止时,正执行的程序不能被恢复执行。

中断包括可屏蔽中断和不可屏蔽中断。CPU提供了两条外接引脚用于中断,即NMI和INTR。其中,NMI用于不可屏蔽中断,比如电源掉电,一旦产生,CPU

必须立即无条件响应,否则进行其他工作毫无意义,INTR用于可屏蔽中断,主要是外部I/O设备的中断信号,比如打印机中断,这些中断信号需要通过中断

控制器传递给CPU。可屏蔽中断有屏蔽和非屏蔽两种状态,处于屏蔽状态的中断将被忽略。

可屏蔽中断受EFLAGES寄存器的中断允许标志位IF控制。


PIC vs APIC


中断控制器有可编程中断控制器和高级可编程中断控制器两种。

前者只能用于单处理器平台,而后者则可以用于多处理器平台。目前,无论UP平台还是MP,多数使用的都是APIC。


1.可编程中断控制器8259A

单个8259A芯片只能管理8个中断源,若是需要管理更多的中断源,则必须将多个芯片级联。虽然最多可以级联成64级,但传统的PIC都是采用两个8259A

级联的方式,支持15个中断源(第一个8259A芯片的IR2引脚用于串联第二个8259A芯片)。

中断控制器与其他硬件设备相连接的各条线被称做中断线。由于数目有限,所以中断线是非常宝贵的资源,使用之前,必须首先在设备的驱动程序中进行

中断线的申请,获得该中断线的控制权,才能发送中断信号。

中断线又被称为IRQ(Interrupt  ReQuest)线,只有当设备确实需要使用中断的时候才能申请占用,或者在申请时采用与其他设备共享中断线的方式。

中断线按照一定的顺序进行编号,这个编号即是中断号。

8259A的输出连接到CPU的INTR引脚上。外设发出中断信号时,若对应的中断没有被屏蔽,则拉高INTR引脚通知CPU有中断发生,CPU可以通过INTA

引脚应答,表示收到中断请求。



2.高级可编程中断控制器APIC

与8259A相比,APIC最大的优势是可适用于MP平台,以及可支持更多的中断源。

APIC由两部分组成:本地高级中断控制器(Local APIC,LAPIC),位于CPU中,主要负责传递中断信号到指定的处理器,在MP平台,每个CPU都具有一个

自己的LAPIC;I/O高级中断控制器(I/O APCI,IOAPIC)负责收集来自I/O设备的中断信号并分发给LAPIC,系统中最多可以拥有8个I/O APIC。

在一个典型的MP平台上,通常有一个IOAPIC和多个LAPIC,所有的LAPIC都连接到I/O APIC,形成一个多级的APIC中断分发网络。


中断号 vs 中断向量


无论是中断还是异常,CPU的响应过程基本一致,即根据中断源所提供的中断向量,从中断描述符表中获取相应处理程序的地址,然后执行。

Intel X86能够支持256种不同的中断,并将它们从0~255进行编号,用于区分不同的中断源。这个8位的编号通常被称为中断向量。

与中断号不同,中断向量是从CPU角度看到的中断信号划分。当I/O设备把中断信号发送给中断控制器时,与之关联的是一个中断号;而当中断控制器将

该信号传递给CPU时,与之关联的则是一个中断向量。


中断号与中断向量之间存在一对一的映射关系。对于8259A,缺省是中断号加上32等于对应的中断向量。


中断服务程序


中断服务程序必须在设备驱动程序中定义,并在使用request_irq函数申请IRQ线时,关联到所申请到的IRQ线上。它们都具有如下相同的原型。

中断服务程序的返回值是一个特殊类型——irqreturn_t,它在include/linux/irqreturn.h文件中定义。

irqreturn_t只能取两个值:IRQ_NONE表示不处理所收到的中断请求,比如当中断服务程序发现该中断请求并不由自己负责时:IRQ_HANDLED表示

接收到了有效的中断请求,并且做出了正确的处理。另外,也可以使用宏IRQ_RETVAL(x),若x为非0值,返回IRQ_HANDLED,否则返回IRQ_NONE.

中断服务程序是无需重入的。当一个给定的中断服务程序正在执行时,相应的中断线在所有服务器上都会被屏蔽,以避免在同一中断线上接收另一个新

的中断。因此,同一个中断服务程序绝对不会被同时调用。


重要数据结构


内核中用于支持中断处理的数据结构主要有3个,分别是irq_chip,irq_desc和irqaction。


中断描述符irq_desc


数据结构irq_desc用于描述IRQ线的属性与状态,又被称为中断描述符(区别于之前所述的门描述符)。

每个IRQ都有它自己的irq_desc对象,所有的irq_desc对象组织在一起形成irq_desc数组,即中断描述符数组。

irq_count与irqs_unhandled字段分别保存了中断发生的总次数与未处理中断的总次数。未处理中断是指没有被内核处理的中断,即与该IRQ线相关

的所有中断处理程序都认为不是自己设备产生的中断,因此拒绝对其进行处理。


中断控制器描述符irq_chip


数据结构irq_chip用于描述不同类型的中断控制器。Linux可以支持多种中断控制器,所以内核分别创建了多个irq_chip结构对象,与之相对应,比如,

I8259A对应i8259A_chip,APIC对应ioapic_chip,lapic_chip等。


中断服务程序描述符irqaction


如前所述,多个设备可以共享同一个IRQ线,但是这些设备都有各自不同的中断处理方式。这就要求我们在进行中中断处理时,能够区分共享同一个

IRQ线的多个设备,基于这个目的,内核引入了数据结构irqaction来描述针对特定设备所产生中断的操作。


中断子系统初始化


Linux的中断处理机制主要包括以下3个方面的内容。

中断子系统初始化:内核在自身初始化过程中对中断处理机制的初始化,包括中断描述符表的初始化以及中断请求队列的初始化等内容。

中断或异常处理:一个实际中断或异常的处理过程。

中断API:为设备驱动程序提供的一组API,包括注册与释放、激活与禁止等函数。


中断描述符表的初始化


中断描述符表的初始化

中断描述符表IDT需要经过两个阶段的初始化,第一阶段初始化发生在内核引导过程,第二阶段初始化发生在内核初始化过程。

1.第一阶段初始化

第一阶段初始化主要完成两项工作:为IDT分配2KB大小的空间(256个中断向量,每个中断向量对应一个8bit的门描述符),并初始化为默认值;

存储IDT的起始地址到IDTR寄存器。

2.第二阶段初始化

内核在自身初始化过程的start_kernel函数中,使用了两个函数trap_init和init_IRQ来完成IDT的第二阶段初始化。其中trap_init函数完成对系统

保留中断向量(异常、非屏蔽中断以及系统调用)的初始化。init_IRQ函数则完成其余中断向量的初始化。

(1)函数trap_init

通过trap_init函数的定义可以看出,内核主要使用了set_intr_gate,set_trap_gate,set_system_gate,set_system_intr_gate以及

set_task_gate等5个函数初始化IDT中的门描述符。

Linux主要使用的是中断门和陷阱门,陷阱门又被细分为陷阱门和系统门。

中断门使用set_intr_gate函数设置,传递给_set_gate函数的type参数值为DESCTYPE_INT。

DESCTYPE_INT表示中断门描述符,特权级为0。因此,对于用户应用程序来说,因为CPL为3,大于中断门的DPL,所以不可能通过某种途径,

比如调用一些汇编指令,穿过中断门去进入中断服务程序。而对于中断来说,中断门DPL被忽略,总是能通过。

陷阱门使用set_trap_gate函数设置,传递给_set_gate函数的type参数值为DESCTYPE_TRAP。DESCTYPE_TRAP表示陷阱门,特权级也为0.

系统门使用set_system_gate函数设置,与陷阱门不同的是特权级为3,因此用户应用可以通过调用某些指令,穿过系统门进入中断服务程序。

(2)函数init_IRQ


中断请求队列的初始化


init_IRQ函数通过调用pre_intr_init_hook函数,并最终调用init_ISA_irqs函数初始化中断控制器以及每个IRQ线的中断请求队列。


中断或异常处理


中断处理基本过程:首先设备产生中断,并通过中断线将中断信号送往中断控制器。如果该中断没有被屏蔽,则会被送往CPU的INTR引脚。CPU

立即停止当前的工作,根据从中断控制器获得的中断向量号,从IDT中找到相应的门描述符,从而获取中断服务程序的地址并执行。

异常处理基本过程:与中断不同,异常并不需要经过中断控制器转发电信号。当异常发生时,CPU通过其特定的中断向量号,从IDT中查找相应

的门描述符,并获取异常服务程序的地址。


中断控制器的工作


8259A主要有3个寄存器用于对中断的响应与处理。

IRR:即Interrupt Request  Register,中断请求寄存器,共8bit,对应IR0~IR7八个中断引脚。当某个引脚的中断请求到来后,若该引脚没有被屏蔽,

则IRR中对应的bit被置为1,表示已经收到设备的中断请求,但还未提交给CPU。

ISR:即Interrupt  Service  Register,中断服务寄存器,共8bit,对应IR0~IR7八个中断引脚。当IRR中的某个中断请求被送往CPU后,ISR中对应的

bit被置为1,表示中断已提交给CPU,但CPU还未处理完。

IMR:即Interrupt  Mask Register,中断屏蔽寄存器,共8bit,对应IR0~IR7八个中断引脚。当某个bit置为1时,对应的中断引脚被屏蔽。


8259A的各个中断引脚之间有一定的优先级,同时也有两种优先级管理方式:固定优先级与循环优先级。固定优先级指优先级固定不变,号码越小

优先级越高。循环优先级指优先级在系统工作过程中可以动态改变。


8259A支持两种中断嵌套模式:一般嵌套模式与特殊嵌套模式。一般嵌套模式是指,当一个中断正在被CPU处理时,优先级等于或低于该中断的被自动

屏蔽,而更高优先级的中断则会被立即送往CPU。特殊嵌套模式与一般嵌套模式的区别为,当一个中断正在被CPU处理时,同级的中断不被屏蔽。Linux

默认使用一般嵌套模式。

下面是8259A在中断处理过程中所要完成的工作:

中断请求。设备发起中断时,与其相连的IR引脚上产生电信号,若对应的中断没有被屏蔽,即IMR中相应位为0,则设置IRR中的相应位为1,并通过INT

引脚向CPU的INTR引脚发出中断请求信号。若已经被屏蔽,则仅仅丢弃该中断请求。

中断相应。CPU响应中断必须满足3个条件:至少有一个中断请求;CPU允许中断;当前指令执行完毕。因此,当8259A向CPU提交中断请求信号时,

CPU可能正在执行一条指令,并不会立即进行响应。此时,可能有其他IRQ线也产生了中断请求,如果没有被屏蔽,那么这些请求对应的IRR位也会被设置为1.


而当CPU执行完一条指令时,回去检查INTR管脚是否有信号,即是否有新的中断请求,如果有,还要检查EFLAGS寄存器的中断允许标志IF是否为1,若IF为1,

则通过INTA引脚应答8259A,表示收到中断请求。

优先级判定。8259A收到CPU的应答后,在IRR中挑选优先级最高的中断,将其在ISR中的对应位置1,并将IRR中相应位置0,表明此中断正在接受CPU的处理。


提交中断向量。CPU通过INTA引脚第二次发出脉冲,8259A收到后根据被设置的起始向量号(通过初始化命令寄存器ICW被初始化),计算最高优先级中断的中断

向量,并将它通过数据线D0~D7提交给CPU。


中断结束。提交中断向量之后,8259A会检测是否AEOI模式,若是,则自动清除中断请求信号,将ISR中的相应位清零。若不是,则需要等待CPU发送EOI指令。

收到EOI后,ISR中的相应位才会被清零。




CPU的工作

如图6.11所示为中断或异常发生时,CPU所要做的工作。

确定中断或异常的中断向量i(在0~255之间)。可屏蔽中断的的中断向量从中断控制器获得,不可屏蔽中断和异常的中断向量是固定的。


通过IDTR寄存器找到IDT,读取第i项(即第i个门描述符)。


进行特权级检查。将CPU的当前特权级CPL与门描述符的DPL相比较,如果CPL小于DPL,则能够通过门,否则产生通用保护异常(中断向量13)。

通过门之后,获取门描述符中段选择符所指向代码段的DPL,将其与CPL相比较,如果小于,则执行中断处理程序,否则产生一个通用保护异常。


若上述过程检测到特权级发生了变化,则需要进行堆栈的切换。因为中断或异常服务程序运行在内核态(特权级为0),当中断或异常在CPU处于用户态(特权级为3)

时发生,将引起堆栈的切换。也就是说,从用户态堆栈切换到内核态堆栈。


如果当前是异常,则CPU将异常码压入当前的堆栈。


如果当前是中断,CPU清零EFLAGS的IF位,即关闭所有的可屏蔽中断;如果是异常,CPU不会清零该位。


进入中断或异服务程序并执行。




内核对中断的处理


所有中断(除去不可屏蔽中断)的服务程序在init_IRQ函数中都被初始化Interrupt[i],interrupt数组的每一项指向一个代码片段,该代码片段除了将中断向量

压入堆栈外,还要调用到一个公共的处理程序common_interrupt。


common_interrupt函数首先保存现场,将中断发生前所有寄存器的值(属于被中断的进程)保存在堆栈中,然后调用do_IRQ函数。

从堆栈中取出中断向量号之后,do_IRQ函数。


从堆栈中取出中断向量号之后,do_IRQ函数调用irq_enter进入中断上下文。irq_enter函数主要的工作是增加被中断进程的preempt_count计数器的值,表示

禁止抢占。在中断处理结束调用irq_exit退出中断上下文时,会相应地减少该计数器的值。


do_IRQ函数的工作重心在第143行,调用每个IRQ线的公共服务程序。

在通用IRQ层引入之前,每个IRQ线并没有自己单独的服务程序,而是所有的IRQ线都公用一个公共的服务程序_do_IRQ对中断进行处理。

在通用IRQ层引入之后,对不同类型中断(边沿触发、电平触发等)的处理流程做出了区分,并分别设置了一些对应的服务程序,比如边沿触发中断对应

handle_edge_irq函数,电平触发中断对应handle_level_irq函数等。

无论是旧的_do_IRQ函数还是新的各种类型中断单独的服务程序,它们的基本原理还是一样的。都要首先屏蔽当前的IRQ,禁止该中断线上的中断传递。

然后,会判断该IRQ线的中断请求队列上是否为空,即是否有一个或多个有效的中断服务程序,如果有的话,就调用handle_IRQ_event函数来遍历并执行挂载

在该IRQ线中断请求队列上的所有中断处理程序。

中断处理结束之后,do_IRQ函数返回到common_interrupt,然后调用汇编函数ret_from_intr从中断返回。

如果内核正在从中断返回到用户空间,并且被中断进程设置了标志TIF_NEED_RESCHED,则ret_from_intr会调用schedule函数进行重新调度。如果内核

正在返回内核空间,即中断了内核本身,则ret_from_intr会检查被中断进程的preempt_count计数器是否为0,只有为0时,schedule函数才会被调用。


内核对异常的处理


与中断不同,各种异常都具有固定的中断向量以及固定的异常服务程序(在trap_init函数中指定)。因此,当异常发生时,将直接跳转到相应的服务程序并执行。



中断API


注册和释放


IRQ线是一个非常宝贵的资源,驱动程序在使用前必须要先注册申请,不再使用时必须要释放申请的IRQ。


1.request_irq函数


request_irq的主要任务是为IRQ线的中断请求队列创建irqaction节点。因为中断请求队列在中断子系统的初始化过程中被初始化为空,所以如果设备没有

使用request_irq函数为其填充节点,即使设备产生了中断,也得不到任何真正的处理。

request_irq返回0表示成功,此时该IRQ被激活。返回非0值,表示发生了错误,常见的错误为-EBUSY,表示指定的IRQ已经被占用,或者是没有设置

IRQF_SHARED标志(与其他设备共享同一条中断线时)。

内核接收一个中断后,将依次调用在该中断线上注册的每一个中断服务程序。设备驱动程序必须知道它是否应该为这个中断请求负责。因此,一个中断

服务程序如果判断与它相关的设备并没有产生该中断请求,那么它应该立即退出。通常硬件设备都会提供状态寄存器(或类似机制),以供中断服务程序进行

检查。

为了避免中断处理程序在设备初始化完成之前就开始执行,初始化硬件和调用request_irq注册IRQ线的顺序必须正确。


2.free_irq函数


卸载设备驱动程序时,必须调用free_irq函数删除中断请求队列中的相应irqaction节点,并释放中断线。

如果IRQ线不是共享的,则free_irq删除irqaction节点之后,禁止该IRQ线。否则,free_irq只删除参数dev_id指定的irqaction节点,而该IRQ线在中断

请求队列中的所有irqaction节点都已经被删除时,才会被禁止。

free_irq直到该IRQ线上的所有中断服务程序都执行完毕时才返回。


激活和禁止


local_irq_disable()                                                      禁止当前CPU的中断

local_irq_enable()                                                       激活当前CPU的中断

local_irq_save(unsigned  long)                               禁止当前CPU的中断并保存标志寄存器的内容

local_irq_restore(unsigned  long)                           回复当前处理器的中断状态,必须与local_irq_save在同一个函数中被调用

disable(unsigned  intrirq)                                          禁止指定的中断线,在函数返回前,确保该中断线上没有中断服务程序在执行

enable_irq(unsigned  int irq)                                     激活指定的中断线

disable_irq_nosync(unsigned int irq)                     调用irq_chip的disable成员禁止指定的中断线,但并不确保该中断线上所有已开始执行的中断服务程序已全部退出


其他API函数


in_irq()                              判断内核是否正在执行中断服务程序

in_softirq()                       判断内核是否正在处理软中断

in_interrupt()                   判断内核是否处于中断上下文,如果内核正在执行中断服务程序或下半部处理程序,则返回非0


通用IRQ层


GenIRQ共抽象为3层:中断控制器硬件操作、IRQ流程处理以及驱动API


1.中断控制器硬件操作


新的irq_chip结构取代了旧的hw_interrupt_type结构,并增加了一些新的底层硬件操作成员函数,包括mask、mask_ack、unmask、set_wake。

基于irq_chip结构的众多成员函数,内核实现了对中断控制器更为精细的管理。


2.IRQ流程处理


在irq_desc结构中,irq_chip指针取代了旧的hw_interrupt_type指针,同时添加了一个新的函数指针handle_irq,指向处理该IRQ线的实际服务程序,

这样就摒弃了之前对所有中断都采用一个服务程序_do_IRQ的做法。

中断之间最大的不同在于如何产生和被处理,GenIRQ定义了4种IRQ Flow类型。

Level-triggered(电平触发)中断,只要设备驱动了中断线就会被激活。它们在处理时必须被屏蔽,并仅仅在设备放弃中断线之后被unmask。

Edge-triggered(边沿触发)中断,在中断线发生改变时产生。它们在处理时不需要被屏蔽,但如果没有被屏蔽,在前一个中断处理结束之前,更多的

中断将到达。因此,内核必须跟踪正在pending的中断,中断服务程序必须循环处理所有的中断。

Simple中断不需要任何特别的控制,能够直接被处理。

Per CPU中断被绑定于单个CPU上。它们类似于Simple中断,甚至更加简单,因为中断服务程序仅仅运行在同一CPU上,不需要使用任何锁机制。


3.驱动API


在驱动API层,GenIRQ的变化相当少,因此原有的驱动一般不需要做出改变即可使用。但仍然有一些新的特点需要被了解。

(1)有一些新增标志用于request_irq函数。

SA_TRIGGER_LOW和SA_TRIGGER_HIGH:用于电平触发方式的中断,指示中断是发生在低电平还是高电平。

SA_TRIGGER_FALLING和SA_TRIGGER_RISING:用于边沿触发方式的中断。

(2)新增如下函数用于直接更改一个IRQ的Flow类型。

int  set_irq_type(unsigned int irq,unsigned int type);

type可以取IRQ_TYPE_EDGE_RISING、IRQ_TYPE_EDGE_FALLING、IRQ_TYPE_EDGE_BOTH,IRQ_TYPE_LEVEL_HIGH,IRQ_TYPE_LEVEL_LOW、

IRQ_TYPE_SIMPLE和IRQ_TYPE_PERCPU之中的一个。

(3)某些设备可以产生将系统从suspend状态唤醒的中断,比如通过keypad中断唤醒系统。GenIRQ增加了下面的函数来激活或者禁止中断控制器的这个特点:

int  set_irq_wake(unsigned int irq,unsigned int on);



多处理器系统中的中断处理


在多处理器系统中,操作系统需要在多个处理器间协调操作,这通常是通过处理器间中断(IPI)实现的。


中断亲和力是指将一个或多个中断服务程序绑定到特定的CPU上运行。


中断负载均衡是指将重负载CPU上的中断迁移到较空闲的CPU上进行处理。如果希望系统支持这个功能,必须在编译内核的时候配置CONFIG_IRQBALANCE选项。


中断的下半部


内核最初通过BH机制实现下半部,定义了一个函数指针数组,共有32个函数指针,采用数组索引来访问与之相对应的一套函数(也就是bottom half)。

同时内核定义了一组枚举变量,包含了这个数组中多数位置所对应的意义。并约定某个驱动使用某个bottom  half位置,比如串口中断就约定使用SERIAL_BH。


内核引入了任务队列(task queue)来取代BH机制,每个队列中都包含一个由延缓执行的函数组成的链表。内核使用struct tq_struct描述延缓执行的任务,使用时

驱动可以使用DECLARE_TASK_QUEUE宏定义自己的任务队列,然后定义一个struct tq_struct变量,并将其注册到自己定义的任务队列上,之后可以通过手工

调用run_task_queue()函数启动该任务队列中的所有任务,也可以直接将run_task_queue()的地址作为BH机制中函数指针数组中的一个函数指针,进而让

BH机制在合适的时候自动执行延缓的任务。


软中断在编译期间静态分配,内核定义了一个软中断数组,最多可以包含32个软中断(由struct  softirq_action描述),每个被注册的软中断都占据该数组中的一项,

但与BH机制相比,它们可以在所有的CPU上同时执行,即使是两个相同类型的软中断,所以使用时需要特别小心。同时,内核还定义了一组枚举变量,预定义了

几个软中断的用途。

一个软中断只能被中断服务程序抢占,而不会去抢占其他软中断。通常中断服务程序会在返回前触发它的软中断以使其在稍后被执行,然后内核在执行完该中断

服务程序后,就会马上调用do_softirq()函数,执行软中断去完成剩余的任务。


tasklet是利用软中断实现的一种下半部机制。与软中断相比,两个不同类型的tasklet可以在不同的CPU上同时执行,但相同类型的tasklet却不能够同时执行,

因此tasklet的锁保护要求较低,这就大大降低了驱动开发的难度。


tasklet本身就是软中断,可以是HI_SOFTIRQ和TASKLET_SOFTIRQ两者之一,它们之间的唯一区别就是HI_SOFTIRQ类型的软中断要比TASKLET_SOFTIRQ

类型的高。当内核调度一个tasklet时,就会唤起这两个软中断中的一个,随后该软中断会被特定的函数进行处理,执行所有已调度的tasklet。


工作队列是另一种实现下半部的机制。与前面讨论的软中断和tasklet机制不同的是,工作队列把延缓的工作交由一个内核线程去执行,因此通过工作队列执行

的代码是运行在进程上下文的,允许重新调度和睡眠。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值