中断处理函数_Linux的中断处理机制 [五] - softirq(2)

ead5b7b62e574a1325b856c166ddb64a.png

Linux的中断处理机制 [四] - softirq(1)

ksoftirqd

"ksoftirqd"是一种per-cpu的内核线程,当你用"ps"命令查看的时候,你会发现ksoftirqd的数目刚好等于你机器的CPU的数目。

ab4bf954c09cbb1d6ffe3906ca537f6a.png

引入ksoftirqd的初衷是为了避免上文提到的“线程饥饿”,在一定程度上提高系统的实时性。如果需要更进一步的提高实时性,应该支持在中断处理函数返回之后,率先执行更高优先级的任务,而不是之前被中断打断的那个任务。

0937e07f98bdef34ae6f6931aeed376f.png

ksoftirqd是作为一个线程参与内核调度的,因而是运行在进程上下文,但在ksoftirqd运行期间,通常也是不允许睡眠的,具体原因将在本文的后半部分给出。

softirq的抢占

softirq和top half/hardirq的一个重要区别是,在hardirq执行期间,硬件中断是被屏蔽的,这样hardirq就不会嵌套(因而hardirq不需要考虑可重入的问题),但是在softitq执行期间,硬件中断是打开的,也就是说,softirq可能被hardirq抢占。

0d0ee82a0ec9aaac21be5a29c364de47.png

自从Linux 2.6.32之后,中断处理函数不再使用被其打断的线程的栈,而是使用独立的per-cpu的栈,并且softirq和hardirq会有不同的栈,这意味着每个CPU都对应一个softirq的栈和一个hardirq的栈。

DEFINE_PER_CPU(struct irq_stack *, hardirq_stack);
DEFINE_PER_CPU(struct irq_stack *, softirq_stack);

一个softirq在被硬件中断打断后,softirq_stack会记录当前softirq的上下文(入栈),然后CPU转去执行hardirq里的程序,同时指向softirq_stack的栈指针也会转而指向hardirq_stack。

hardirq在执行完毕后,会触发其对应的softirq,但并不会立即执行这个softirq,而只是将这个softirq在pending位图中对应的bit置位。然后CPU会跳回之前被打断的那个softirq继续执行,softirq_stack保存的上下文也将被恢复(出栈),直到这个被打断的softirq执行完毕,内核才会根据softirq的pending位图重新选择待处理的softirq来执行。

可见,softirq只会被硬件中断抢占,而不会被另一个softirq抢占。如果不考虑内核的实时特性和中断线程化,那么softirq也不会被更高优先级的线程所抢占。

在多核系统中,同一种softirq(跟信号一样,编号相同的就是同一种)的处理函数可以在不同的CPU上运行,这样可以利用多核的并行特性,带来更好的性能,但是如果sofirq的处理函数中含有全局变量,就可能涉及到spinlock等多核同步机制,增加复杂性。对此,下文将要介绍的tasklet会有不同的规则和处理方式。

softirq的屏蔽

线程在执行过程中,可能会被sofirq打断。这里的“被softirq打断”并不意味着线程真的会像被硬件中断打断那样,直接跳转到hardirq的处理函数去执行,而是指如果线程被硬件中断打断了,执行完hardirq后,就直接返回原来的线程,而不是按正常流程去执行pending的softirq。

从实现的角度来看,这个机制跟“被信号打断”的处理手法是基本一样的。因而就像可以用sigprocmask()屏蔽信号一样,如果线程不希望被softirq打断,那么可以调用local_bh_disable()函数来屏蔽softirq。

8678cfd834870c1d608e1d925f6a21a7.png

在local_bh_disable()期间,线程虽然屏蔽了softirq,但可能没有屏蔽硬件中断,而如果发生了硬件中断,又将会产生pending且不能执行的softirq,所以在调用local_bh_enable()打开softirq时,需要检测当前是否有处于pending状态的待处理的softirq。

执行被local_bh_disable()和local_bh_enable()包围的代码区域时,由于softirq是被屏蔽的,因而在这段时间里也是不能睡眠的,也是处在“softirq上下文”。虽然都是在softirq上下文,但还是有办法将这种情况和softirq正在运行的情况区分开来,靠的也是这篇文章介绍的preempt_count()。

local_bh_disable();
/* critical section */
local_bh_enable();

前面在介绍ksoftirqd的时候留了一个问题,为什么在ksoftirqd线程执行期间也不允许睡眠?因为进入ksoftirqd之后,softirq也是被屏蔽的,相当于是执行了local_bh_disable()。这样设计的目的是考虑到同一段处理softirq的代码,既可能在__do_softirq()中执行,又可能由于强制线程化,或者超过执行时间/次数的限制,被挪到ksoftirqd中执行。如果两者使用不一样的规则,将会增加代码移植的开销(参考这个回答)。

线程既可能被softirq抢占,也可能被硬件中断抢占,而softirq通常只会被硬件中断抢占,如果在线程或者softirq执行期间不希望被硬件中断抢占,那么可以使用local_irq_disable()/local_irq_save()函数。

fc3f8e640d81904ce185717eaa09fbab.png

至此,softirq就介绍完了,下文将开始介绍tasklet和workqueue。

参考:

Interrupts -5- (Softirq)

linux kernel的中断子系统之(八):softirq

原创文章,转载请注明出处。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值