1.为什么要有软中断
中断处理一般可以分为两个部分,一是必须及时处理的,例如收包时要及时将收到的包放入缓冲区,否则会丢失;另一类是对实时性要求不那么高的,例如从缓冲区取出数据包,送给高层协议进行处理。在中断处理中一般是要关闭中断的,因此如果中断处理占用时间很长,就有可能造成无法及时处理其他的中断,而造成丢包。
为了解决这个问题,引入了软中断的概念。必须要及时处理的事情在中断处理中进行,而后续处理在软中断中进行(软中断是可以被抢占的)。
2.注册软中断
使用open_softirq注册中断处理函数。
void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
{
softirq_vec[nr].data = data;
softirq_vec[nr].action = action;
}
3.设置软中断
相关的函数:
void fastcall raise_softirq(unsigned int nr)
{
unsigned long flags;
local_irq_save(flags);
raise_softirq_irqoff(nr);
local_irq_restore(flags);
}
inline fastcall void raise_softirq_irqoff(unsigned int nr)
{
__raise_softirq_irqoff(nr);
if (!in_interrupt())
wakeup_softirqd();
}
#define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)
可以看出__raise_softirq_irqoff就是设置了一个标志位,此后在__do_softirq中检查该标志位,并执行注册的函数。
4.软中断的执行
asmlinkage void do_softirq(void)
{
__u32 pending;
unsigned long flags;
if (in_interrupt())
return;
local_irq_save(flags);
pending = local_softirq_pending();
if (pending)
__do_softirq();
local_irq_restore(flags);
}
可以看出,当处于中断上下文时,不会执行软中断。否则取得当前设置的softirq的bitmap,如果存在需要处理的软中断,则调用__do_softirq处理。下面再看看__do_softirq:
asmlinkage void __do_softirq(void)
{
struct softirq_action *h;
__u32 pending;
int max_restart = MAX_SOFTIRQ_RESTART;
int cpu;
pending = local_softirq_pending();
account_system_vtime(current);
__local_bh_disable((unsigned long)__builtin_return_address(0));
trace_softirq_enter();
cpu = smp_processor_id();
restart:
/* Reset the pending bitmask before enabling irqs */
set_softirq_pending(0);
local_irq_enable();
h = softirq_vec;
do {
if (pending & 1) {
h->action(h);
rcu_bh_qsctr_inc(cpu);
}
h++;
pending >>= 1;
} while (pending);
local_irq_disable();
pending = local_softirq_pending();
if (pending && --max_restart)
goto restart;
if (pending)
wakeup_softirqd();
trace_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
可以看出,该函数会取得当前softirq的bitmask,之后设置pending为0,之后执行处理函数。在处理期间,可能有其他的软中断进行了设置,所以处理完pending后还要再次取得当前的bitmask,如果存在软中断,并且未达到执行限额MAX_SOFTIRQ_RESTART;,则继续处理软中断。
5.关于ksoftirqd内核线程。
之所以要有该内核线程,是因为软中断的优先级高于用户进程,如果软中断执行过多,会造成用户进程无法执行。因此引入了ksoftirqd线程,它的优先级为最低的19。细节就不看了,总之里面会调用do_softirq处理中断。
6.关于软中断的执行时间
在处理中断的do_irq的最后有irq_exit函数,在中断处理完后接着处理软中断。
void irq_exit(void)
{
account_system_vtime(current);
trace_hardirq_exit();
sub_preempt_count(IRQ_EXIT_OFFSET);
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
preempt_enable_no_resched();
}
其中会检查是否要处理软中断,如果需要则调用invoke_sofirq,其实就是do_softirq或者__do_softirq。
此外在local_bh_enable中也会调用do_softirq,他的作用是开启bottom half处理(softirq或tasklet),并处理需要处理的softirq。