分类: LINUX
2012-07-13 19:44:06
cpu与磁盘、网卡、键盘等外围设备(相对于cpu和内存而言)交互时,cpu下发I/O请求到这些设备后,相对cpu的处理能力而言,磁盘、网卡等设备需要较长时间完成请求处理。
那么在请求发出到处理完成这段时间,应如何设定cpu的行为,既能让这期间运行的其他程序得到执行,又能在外设处理完成后,cpu及时获取到处理完成的消息?
可以按以下方式设定cpu行为:
对于第1种方式,轮询会带来不必要的cpu消耗。第2种是现行的方式,外设通过电信号告知cpu的机制被称为中断(interrupt)。
IRQ line与ISR
不同设备发起的中断以不同的数值标识,这些标识中断的数值被称为中断请求线(interrupt request line, IRQ line),IRQ line与中断的关系相当于系统调用号与系统调用的关系。
cpu接收到中断后会将中断递交给内核处理,内核中有相应的函数完成中断处理(interrupt handler或interrupt service routine,ISR)。
中断上下文
每个中断对应一个中断处理函数,当中断处理函数被调用时,内核处于中断上下文(interrupt context)。不同于进程上下文(process context),中断处理过程中不能发生阻塞、休眠,即不进行进程调度,其基于以下原因:
中断屏蔽
从是否可屏蔽的角度,中断可分为:
对于可屏蔽中断,关闭中断的方式有以下3种:
因中断是异步的,屏蔽中断可用于防止中断嵌套。非正常的中断关闭会带来很多不良结果,例如cpu不响应键盘中断时,用户无法使用键盘操作;又例如cpu不响应时钟中断,则不能进行进程调度,依赖于时钟中断的任务都无法完成,机器基本变成僵尸。
中断处理流程
外设发起中断,cpu接收中断并传递给内核处理,下图说明了该过程:
内核代码中,中断号被传递到do_IRQ处理,do_IRQ调用__do_IRQ函数,__do_IRQ中调用handle_IRQ_event函数,其调用相应的中断处理函数进行中断处理;处理完成,返回到do_IRQ函数,之后调用irq_exit,在irq_exit函数中,调用do_softirq处理软中断,软中断相关内容将在后文讲述。
proc接口
proc文件系统向用户开放了各个中断的情况:
以上输出显示了中断号、各个cpu接收中断次数、中断控制器(interrupt controller)和中断名称等信息,从/proc/stat也可查到中断相关信息。
cpu亲和力
多核cpu给中断处理方式带来了新的问题:若某个中断到来,应该由哪个核处理?
我们可以通过proc的接口,设定特定的cpu处理某个中断,这被称为cpu对中断的亲和力(affinity),利用/proc/irq/{interrupt num}/smp_affinity可以查看或设定cpu亲和力:
以上查询结果显示,0号、1号cpu均可处理0号中断,而1号中断更倾向于交给1号cpu处理。若要设定1号cpu处理1号中断,可执行以下命令:
中断负载平衡
irqbalance为用户态下的一个守护进程,其可平均地在多核cpu间分发中断:
下半部
内核接收到cpu传递过来的中断后,为做到快速响应外设,中断的处理被分成两部分:
例如网卡接收数据过程中,首先网卡发起中断告诉cpu取数据,然后内核从网卡读取数据存入缓存,再之后内核解析数据并将数据送到应用层。如果以上工作都让中断处理程序来处理,过程太长,会导致丢失新来的中断。更优的方式是将以上工作分成两部分,从网卡读取数据到缓存由上半部完成,解析数据等较不紧急的工作由下半部完成。
下面我们看softirq和tasklet两种下半部实现方式。
softirq
softirq在2.3版本内核被引入,相关代码在
可用的softirq有以下几种,在
提交一个softirq需要调用raise_softirq函数,raise_softirq调用raise_softirq_irqoff,该函数将相应软中断标识为pending、完成软中断提交。
标识为pending的软中断可在以下时机得到处理:
1. 硬中断处理完成、do_IRQ函数即将返回时处理
do_IRQ调用irq_exit函数,irq_exit调用invoke_softirq,即do_softirq函数,do_softirq调用__do_softirq。在__do_softirq函数中,最多处理10个软中断,若软中断的个数超过10,则调用wakeup_softirq唤醒ksoftirqd内核线程进行软中断处理
2. ksoftirqd内核线程被唤醒后处理
以上说明了ksoftirqd线程被唤醒的一种情况,wakeup_softirqd还会在raise_softirq_irqoff函数中被调用,也即软中断被提交之后,可唤醒ksoftirqd完成软中断处理
3. 显式地调用do_softirq进行软中断处理
在内核网络代码中,netif_rx_ni函数会主动调用do_softirq进行软中断处理
tasklet
tasklet基于softirq实现,其本质也是softirq,对应softirq枚举类型中的HI_SOFTIRQ和TASKLET_SOFTIRQ,HI_SOFTIRQ优先级较高。
相比softirq,即使相同类型的softirq也可同时在不同的cpu上处理,而相同类型的tasklet不可同时在不同cpu上处理,不同类型的tasklet可以。
可通过tasklet_schedule或tasklet_hi_schedule提交tasklet,这两个函数最终调用raise_softirq_irqoff提交软中断。
因tasklet提交的同样是软中断,所以还是由do_softirq函数完成tasklet的处理。
Reference: Chapter 7 and chapter 8, Linux kernel development.3rd.Edition