软中断
软中断,softirq,经常听说,但是究竟是什么,怎么用其实我并不清楚。这不,最近看到有代码使用了软中断,为了能够进一步了解只好硬着头皮来看看源代码。
软中断是利用硬件中断的概念,用软件方式进行模拟,实现宏观上的异步执行效果。
那究竟是怎么做的呢?让我们一探究竟。
初始化
一切的一切总有个起头的,既然是抓瞎的状态,那就先抓住开始的部分。
瞧,这其实啥都没干,就填写了一个数组的成员。那这个数组长什么样子呢?
好吧,就这么一个光杆司令,每个数组就是一个毁掉函数的成员。真的是不知所云。
硬塞的__preemt_count
按照正常逻辑,现在我应该来讲什么时候softirq被调用的。但是呢,我发先有一个非常重要的概念(变量)对理解何时调用很关键,所以就硬插进来,模拟一个“软中断”。
这个变量叫__preemt_count。名字很简单,但是定义却藏着玄机。
乍一看就是一个32位的整型,但是你再往里看其实这个整型被切几个小块,没块有自己的含义。
然后我用一张简易图来展示一下上面的定义:
与此同时,就要引出几个和当前cpu运行状态相关的函数了。
in_irq() - We're in (hard) IRQ context
in_softirq() - We have BH disabled, or are processing softirqs
in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
in_serving_softirq() - We're in softirq context
in_nmi() - We're in NMI context
in_task() - We're in task context
原来我们通常判断cpu状态的函数,就是根据cpu上变量__preempt_count来确定的。顿时有种找到根的感觉。
那为什么会插播这么一个变量呢?是因为在softirq中,以及其他很多地方都会判断这个值来确认当前cpu运行状态,以此来判断是否应该执行什么操作。
好了,这个“软中断”结束了,让我们回到上文。
何时调用软中断?
对软中断的调用,还得分成两步:
标记有软中断的请求
在适当的时机执行
毕竟软中断不像硬中断,可以来了就执行。软中断没有硬件的这种权利打断别人的运行,只好先标记好自己的到来,等待时机的出现。
标记软中断请求
标记请求由函数raise_softirq(nr)来完成。
实际上做了什么呢?打开看一看。
关中断
在变量local_softirq_pending_ref上,标记nr
如果不在in_interrupt(),唤醒softirq线程
在这里我们着重要讲的是第二部,标记变量local_softirq_pending_ref。
在适当的时机执行软中断
之前我们也提到,软中断不像硬中断能够要求硬件及时响应。所以只好等到某个时间,再由cpu来处理。那么都有哪些时机,cpu回来处理软中断呢?
irq_exit(): 中断处理函数返回时
local_bh_enable(): 允许软中断时
raise_softirq(): 标记软中断时(通过唤醒线程)
在没有线程化irq时,前两者是立即执行的,只有第三者是通过wakeup来唤醒软中断线程。
这里我们结合一下上一小节硬插进来的概念,看看raise_softirq()中的处理。
而这个in_interrupt()的含义是: We're in NMI,IRQ,SoftIRQ context or have BH disabled。这就是当我们cpu运行在这几个状态下时,我们就不去唤醒软中断了。 为什么呢?因为当我们从中断/软中断返回时,我们会去处理新的这个软中断的。
这就是上一小节引入变量的作用。
响应软中断
终于是时候来看软中断的响应流程了。在上面三个响应软中的地方看下来,最终都会走到函数__do_softirq()。
这个函数就是根据变量local_softirq_pending_ref上标记的软中断号,来依次处理事先注册好的软中断函数。
当然里面有几个点值得关注:
函数__local_bh_disable_ip(RET_IP, SOFTIRQ_OFFSET)和__local_bh_enable(SOFTIRQ_OFFSET) 来表示in_serving_softirq()。
保存好现场后才开中断
好了,大致框架梳理完了。有机会再来细扣其中的细节。
Last updated