softirq部分代码解读

softirq部分代码解读

文中有部分摘抄自其他参考文章:
理解 linux 内核的软中断 https://linux.cn/article-5431-1.html

/proc/softirqs 下的 softirq 显示

某些/proc/softirqs的信息:
在这里插入图片描述
在这里插入图片描述

/proc/softirqs 代码位置

显示代码在linux-4.9.37/fs/proc/softirqs.c文件中,显示打印的函数如下:
在这里插入图片描述

linux-4.9.37/include/linux/kernel_stat.h文件中定义了这个软中断数目增加的函数以及获取软中断数目的函数,在__do_softirq()时会调用kstat_incr_softirqs_this_CPU()函数来进行统计。
在这里插入图片描述

软中断 softirq

主要内容集中在linux-4.9.37/kernel/softirq.c文件中。

软中断向量表

softirq.c定义了一个软中断向量表,中断向量的类型是一个函数指针。
在这里插入图片描述

注册软中断

代码通过open_softirq()函数注册软中断,实际就是将软中断处理函数与软中断编号绑定。
在这里插入图片描述

软中断的激活

每个CPU都有一个32bit的位图(即__softirq_pending)来维护本CPU上的软中断是否激活。
在这里插入图片描述

软中断激活时机之一:irq_exit

irq_exit()函数里可能会激活软中断,激活条件是不在硬中断里也不在软中断里,并且本CPU的__softirq_pending有置位。这个条件可以看到软中断和硬终端是同等对待的(在in_interrupt()里),体现都是中断处理。不能在硬中断里的条件,表明必须优先性,必须硬中断全部处理完,才考虑软中断;不能在软中断里的条件,表明屏蔽了软中断的嵌套。
在这里插入图片描述
invoke_softirq()函数的处理是,要么(先唤醒ksoftirqd)将软中断交由ksoftirqd专门线程处理,要么直接调用__do_softirq即时处理(当然,即时处理要区分是在哪个栈上:是当前栈上还是在独立的软中断栈上)。
在这里插入图片描述
invoke_softirq()调用的一些函数具体定义:
在这里插入图片描述

软中断激活时机之二:raise_softirq

网卡收包方式从非NAPI进化到NAPI方式,就充分展示了软中断的优点:把收报任务最大程度地交给软中断处理,最大程度简化硬中断处理。

raise_softirq()函数会调用__raise_softirq_irqoff()函数,在指定CPU的__softirq_pending位图上置位相应的软中断。raise_softirq_irqoff()函数和raise_softirq()函数的区别是关中断的操作是否已经完成了。置位位图是一个竞争操作,所有硬中断里都可能做,所以得保证在关中断的情况下完成。

在这里插入图片描述

软中断激活时机之三:ksoftirqd

每个CPU都有一个ksoftirqd线程在软中断量大时专门处理软中断。ksoftirqd线程的核心函数run_ksoftirqd()的(循环)处理是:关中断看本CPU的__softirq_pending的置位情况,如有则执行__do_softirqd(),执行完开中断)。这个执行很顺畅,因为是在该线程自己的栈上,不会有影响用户进程的问题。

ksoftirqd里也可以看到,在执行软中断前是可以被抢占的,但是一旦开始执行就不能被抢占了(和上面的调度之一:irq_exit()中的讲述的思想是一致的)。就是说,软中断和硬中断的处理思想是一致的:执行期间不允许发生调度!

上述不能抢占的原因其实就是类似事务性的一个原则:一旦开始不能停止。另外一个原因是,执行的是用户自定义的硬(软)中断程序,操作具有不确定性,如果让这些操作期间具有调度可能,则会脱离内核的控制范围。
在这里插入图片描述

软中断激活时机之四:其他地方

比如netif_rx_ni(),执行do_softirq前关抢占,不能在执行软中断期间调度。
在这里插入图片描述

软中断激活时机之五:local_bh_enable

想想,如果异常和软中断有共享数据的话,异常处理走到此共享数据的临界区时需要关软中断,但不需要关硬中断。那么当走完临界区时,需要开软中断,此时就是一个激活时机(看preempt_count了,其实可能也是一个抢占时机)。

用“激活”而不是“调用”的原因是外围处理仅修改本CPU的__softirq_pending位图,最后由核心机制(比如ksoftirqd、能通过in_interrupt()检查的软中断处理)真正处理,而这就是软中断的理念:让硬中断(或者其它)更快执行,所以不会采用直接调用的方式。

“激活”的原则是谁激活,谁处理,哪个CPU上的硬中断带来的软中断就由哪个CPU处理(或者说,归属CPU是软中断跟着硬中断走)。这样,充分发挥smp的优势,均衡到各个CPU上。至于硬中断和CPU之间的关系,我们以后讲到硬中断时再讨论。每个CPU维护自己的软中断机制就行了,各个CPU是互不相关的。注意,还是有相关性的:各个CPU并行处理同一类型的软中断时,该类型软中断处理需要为共享数据做保护,这是软中断可重入性需要付出的代价。
在这里插入图片描述

处理软中断 do_softirq

do_softirq()先检查软中断重入条件:必须不在硬中断里并且不在软中断里,符合条件之后就可以开始做如下的软中断处理了:
在这里插入图片描述
这个处理是在关中断的保护下完成的,毕竟软中断和硬中断本质上是一样的,都是中断体系的(当然,进入到硬/软中断内部再开则另当别论了)。也可以看到,局部变量pending没有传入__do_softirq()内部,所以此处仅是判断,不是使用,此处判断值和内部使用值可能有差异,位图中置位位数会少一些。

我们再深究一下这个检查条件。我们的理解是:
这个条件达到了两个效果:同一CPU上的软中断不嵌套;嵌套硬中断中不处理软中断。就同一个CPU而言,__do_softirq()函数的执行是串行的,非重入的(do_softirq()函数可以说是可重入的);就多个CPU而言,__do_softirq()函数是可重入的,即使是同一个类型的软中断。也就是说,软中断通过这个检查条件做到了本CPU上的软中断处理串行化,当然,多CPU之间的还是并行的,所以同一类型软中断处理还是需要保护自己的相关共享数据结构的。

__do_softirq()函数处理是尽量(虽然可能还是执行不完)执行所有被激活的软中断(由本CPU上的__softirq_pending位图标识)处理。我们分三个阶段分析。
准备处理阶段:关闭软中断(效果是让上面提到的检查条件为真,从而达到禁止本CPU上的软中断嵌套的目的)。
核心处理阶段:关硬中断,获得本CPU的__softirq_pending位图并存储起来,清空位图,开硬中断(仅在读写位图时需要关硬中断,防止其它硬中断同时操作)。执行本CPU的所有软中断(由存储起来的位图获得)。这个核心处理是个循环,最多10次(MAX_SOFTIRQ_RESTART),毕竟此时用的是用户进程的栈,不能借用太久。退出循环的条件是:总时间超出或者被抢占(开中断就会有被抢占)或者达到10次了。
结尾处理阶段:关硬中断,开软中断。

另外,如果10次循环都解决不完软中断,说明期间发生的硬中断很多,带来的额外的软中断也很多。那么就不继续影响借用的用户进程栈了,直接交给专门的ksoftirqd内核线程处理。这也就说明了循环的含义:处理软中断期间时还会进入新的硬中断,从而带进新的软中断(当然,仅仅是在本CPU的__softirq_pending上置位,不会有实际处理),所以需要反复去处理(处理的目标很明确,就是要清空本CPU上的__softirq_pending位图)。

再看看那个防止软中断嵌套的流程。关软中断中肯定有一句原子地加1的关键语句,如果当前内核路径A在该原子操作之前被另一个内核路径B打断,则B执行完硬中断和软中断后,返回到A的此处,A接着执行该原子操作,之后的软中断处理应该是空转,因为肯定已经被B处理完了。如果在该原子操作之后被B打断,则B执行完硬中断,不会执行自己的软中断而是会直接退出(因为软中断嵌套了),返回到A的此处,A接着执行,这次A除了处理自己软中断,还会额外地处理B的软中断。
对于preempt_count中的软中断位,由上述可以知道,它的作用有两个:防止软中断在单CPU上嵌套;保证了在执行软中断期间不被抢占。
最后,还得重复一句:这里讲的__do_softirq()函数都是在一个CPU上的处理,多个CPU上的并行是不受任何控制的。

关于中断的时序貌似很复杂,但其实都逃不过两个原则:硬中断会打断硬中断(当然是不同类型的);硬中断会打断软中断(同样地:软中断不会打断硬中断,软中断也不会打断软中断)。所有貌似复杂的时序其实都只是这两个的叠加而已。

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 4
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值