小公司研发总监,既当司令也当兵!
分类: LINUX
2018-01-24 11:33:54
软中断介绍
把可以延迟的处理从硬中断处理程序独立出来,这样这个处理可以在开中断的情况下运行,这个处理就是软中断。可见,软中断的这种脱离可以大大缩短硬中断的响应时间,对于很多实时应用来说及其重要。
我们本文只谈软中断,至于tasklet、workqueue等我们以后再谈。我们在讲述软中断流程(参考linux kernel 4.0)时会尝试深入理解其中的各个细节之处,分享我们自己(计算机学习微信公众号:jsj_xx)的理解(如果不正,还望指出,谢谢)。
本文分上下两部分:上部分讲软中断的定义、注册、激活;下部分讲核心的do_softirq函数。
软中断数据结构的定义
软中断目前有10(由NR_SOFTIRQS定义)个,通过softirq_vec[NR_SOFTIRQS]数组来管理这些软中断,全部cpu共用。
软中断的注册
通过open_softirq()将具体的软中断处理函数和软中断编号绑定。如网络系统注册了收发包的软中断处理函数:
open_softirq(NET_TX_SOFTIRQ, net_tx_action); open_softirq(NET_RX_SOFTIRQ, net_rx_action); |
软中断的激活
每个cpu都有一个32bit的位图(即__softirq_pending)来维护本cpu上的软中断是否激活。
typedef struct { unsigned int __softirq_pending; #ifdef CONFIG_SMP unsigned int ipi_irqs[NR_IPI]; #endif } ____cacheline_aligned irq_cpustat_ irq_cpustat_t irq_stat[NR_CPUS] ____cacheline_aligned; |
软中断的激活时机之一:irq_exit
irq_exit函数里可能会激活软中断,激活条件是:
不在硬中断里并且不在软中断里并且本cpu的__softirq_pending中有置位。
if (!in_interrupt() && local_softirq_pending()) invoke_softirq(); |
由这个条件,我们可以知道,软中断和硬中断在这里是同等对待(在in_interrupt里)的,体现都是中断处理这一个本质。不能在硬中断里的条件,表明必须优先性,必须硬中断全部处理完,才考虑软中断;不能在软中断里的条件,表明屏蔽了软中断的嵌套。
invoke_softirq函数的处理是,要么(先唤醒ksoftirqd)将软中断交由ksoftirqd专门线程处理,要么直接调用__do_softirq即时处理(当然,即时处理要区分是在哪个栈上:是当前栈上还是在独立的软中断栈上)。
我们看看即时处理这个流程。local_softirq_pending前肯定会清除preempt_count中的硬中断位,如果此时preempt_count里没有软中断位则可以被抢占(即时关闭硬中断)。在进入到__do_softirq处理各个软中断期间,肯定是禁止抢占了。在硬(软)中断上下文里的抢占是众所周知不被允许的:会让被中断的进程执行时间不确定,也是不公平的(也就是说,不要在硬中断和软中断的处理中有调度离开的意向)。
软中断的激活时机之二:raise_softirq
网卡收包方式从非NAPI进化到NAPI方式,就充分展示了软中断的优点:把收报任务最大程度地交给软中断处理,最大程度简化硬中断处理。这种进化,我们(计算机学习微信公众号:jsj_xx)以后再讲。
raise_softirq函数会调用__raise_softirq_irqoff函数,在指定cpu的__softirq_pending位图上置位相应的软中断。raise_softirq_irqoff函数和raise_softirq函数的区别是关中断的操作是否已经完成了。置位位图是一个竞争操作,所有硬中断里都可能做,所以得保证在关中断的情况下完成。
软中断的激活之三:ksoftirqd
每个cpu都有一个ksoftirqd线程在软中断量大时专门处理软中断:
DEFINE_PER_CPU(struct task_struct *, ksoftirqd); |
ksoftirqd线程的核心函数run_ksoftirqd的(循环)处理是:关中断看本cpu的__softirq_pending的置位情况,如有则执行__do_softirqd(),执行完开中断)。这个执行很顺畅,因为是在该线程自己的栈上,不会有影响用户进程的问题。
这里有个疑问,此处以前是关抢占保护,现在是关中断的保护了(参考2012年的patch 3e339b,softirq: Use hotplugthread infrastructure)?我们的理解是:关抢占的保护方式,会让后续更多的软中断由ksoftirqd处理,不符合ksoftirqd的辅助地位。就处理软中断的地位而言,应该是irq_exit的为主,ksoftirqd的为辅。)
ksoftirqd里也可以看到,在执行软中断前是可以被抢占的,但是一旦开始执行就不能被抢占了(和上面的调度之一:irq_exit中的讲述的思想是一致的)。就是说,软中断和硬中断的处理思想是一致的:执行期间不允许发生调度!
上述不能抢占的原因其实就是类似事务性的一个原则:一旦开始不能停止。另外一个原因是,执行的是用户自定义的硬(软)中断程序,操作具有不确定性,如果让这些操作期间具有调度可能,则会脱离内核的控制范围。
软中断的激活之四:其他地方
比如netif_rx_ni(),执行do_softirq前关抢占,不能在执行软中断期间调度。
上部分讲了软中断的外围基础,我们下部分接着讲核心的do_softirq函数的处理。(未完待续。。。)