Linuxsoftirq
一、软中断产生的原因
软中断:用于有效的实现内核的延期操作,也是底半部机制tasklet的基础
二、数据结构
1)softirq_action
该结构是软中断的核心数据结构,代表软中断处理函数。
structsoftirq_action
{
void (*action)(structsoftirq_action*);
};
2)下面是软中断的类型,目前支持10个软中断类型。
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /*PreferableRCUshouldalwaysbethelastsoftirq*/
NR_SOFTIRQS
};
3)softirq_vec
该数组存储了已注册的软中断类型对应的处理函数,该数组非常重要
staticstructsoftirq_actionsoftirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;
三、软中断的注册
要在内核中使用一个软中断,首先要进行注册,所谓注册,其实就是在数组softirq_vec中增加相应软中断的处理函数,调用函数定义如下:
/*
注册一个软中断,即在软中断向量数据中,增加软中断number为nr的处理函数
*/
voidopen_softirq(intnr,void(*action)(structsoftirq_action*))
{
softirq_vec[nr].action=action;
}
四、启动软中断
如果要启动某个软中断,则需要置位irq_stat[cpu].__softirq_pending中的相应位,而后续的处理工作则由do_softirq处理。
而设置__softirq_pending的操作则由函数__raise_softirq_irqoff来实现。
可以通过以下两种方法启动软中断:
1)在中断上下文中,通过调用函数raise_softirq,置位irq_stat[cpu].__softirq_pending中的相应软中断位,则会在中断结束后在函数irq_exit中调用invoke_softirq,实现软中断处理
2)在非中断上下文中,通过调用raise_softirq_irqoff,置位irq_stat[cpu].__softirq_pending中的相应软中断位,并唤醒软中断守护进程,通过软中断守护进程实现软中断的处理
3)在__do_softirq中,当该函数执行完时还有未决的软中断,则唤醒软中断守护进程,由软中断守护进程继续处理未决的软中断
以上3种方法中,不管是通过调用函数invoke_softirq,还是通过软中断守护进程来处理软中断,最终都会调用函数do_softirq、__do_softirq。
do_softirq主要完成以下工作
1)首先判断当前是否在中断上下文中,若是则直接返回
2)关闭irq,读取当前cpu的irq_stat[cpu].__softirq_pending
3)判断__softirq_pending的值是否为0,若不为0则说明有软中断待处理,则调用__do_softirq
asmlinkagevoiddo_softirq(void)
{
__u32pending;
unsignedlongflags;
if(in_interrupt())
return;
local_irq_save(flags);
pending=local_softirq_pending();
if(pending)
__do_softirq();
local_irq_restore(flags);
}
__do_softirq的定义如下:
其主要完成的工作有:
1)获取irq_stat[cpu].__softirq_pending的值
2)重置irq_stat[cpu].__softirq_pending的值为0,并开启软中断
3)获取软中断向量数据softirq_vec
4)在一个while循环中,对于每一个未处理的软中断,执行softirq_vec中相对应的action处理函数
5)关闭中断,重新读取irq_stat[cpu].__softirq_pending的值,若该值不为0则
6)在重复执行的次数没有超过MAX_SOFTIRQ_RESTART,且irq_stat[cpu].__softirq_pending的值不为0时,重新执行上述2、3、4、5的操作
7)若已超过MAX_SOFTIRQ_RESTART,则调用wakeup_softirqd,唤醒软中断守护进程,由软中断守护进程继续处理
#defineMAX_SOFTIRQ_RESTART10
asmlinkagevoid__do_softirq(void)
{
structsoftirq_action*h;
__u32pending;
intmax_restart=MAX_SOFTIRQ_RESTART;
intcpu;
pending=local_softirq_pending();
account_system_vtime(current);
__local_bh_disable((unsignedlong)__builtin_return_address(0));
lockdep_softirq_enter();
cpu=smp_processor_id();
restart:
/*Resetthependingbitmaskbeforeenablingirqs*/
set_softirq_pending(0);
local_irq_enable();
h=softirq_vec;
do{
if(pending&1){
intprev_count=preempt_count();
kstat_incr_softirqs_this_cpu(h-softirq_vec);
trace_softirq_entry(h,softirq_vec);
h->action(h);
trace_softirq_exit(h,softirq_vec);
if(unlikely(prev_count!=preempt_count())){
printk(KERN_ERR"huh,enteredsoftirq%td%s%p"
"withpreempt_count%08x,"
"exitedwith%08x?\n",h-softirq_vec,
softirq_to_name[h-softirq_vec],
h->action,prev_count,preempt_count());
preempt_count()=prev_count;
}
rcu_bh_qs(cpu);
}
h++;
pending>>=1;
}while(pending);
local_irq_disable();
pending=local_softirq_pending();
if(pending&&–max_restart)
gotorestart;
if(pending)
wakeup_softirqd();
lockdep_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
疑问:在SMP下,多个CPU可以同时执行同一个软中断吗?
解答:因为每一个CPU都有一个__softirq_pending变量,那么在同一时刻多个CPU的__softirq_pending的某个相同位可能都为1,这样的话,就可能出现在同一时刻多个CPU同时执行同一个软中断。
软中断的应用:
在内核代码中,使用软中断的有:在接收和发送网络数据包时,会借助软中断实现;另一个中断底半部处理机制tasklet也使用了软中断,tasklet即使用软中断作为基础,然后再实现tasklet。在我们编写我们自己的驱动代码时,个人建议首选使用tasklet,而不要使用softirq。
出门走好路,出口说好话,出手做好事。