Linux内核定义的软中断如下:
static struct softirq_actionsoftirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
char *softirq_to_name[NR_SOFTIRQS] = {
"HI","TIMER", "NET_TX", "NET_RX", "BLOCK",
"TASKLET","SCHED", "HRTIMER", "RCU"
};
内核启动时注册的软中断有以下。
其注册函数为:
void open_softirq(int nr, void(*action)(struct softirq_action *))
{
softirq_vec[nr].action= action;
}
其实就是对全局定义的数组变量softirq_actionsoftirq_vec[NR_SOFTIRQS]进行初始化。
enum
{
HI_SOFTIRQ=0,
TIMER_SOFTIRQ,
NET_TX_SOFTIRQ,
NET_RX_SOFTIRQ,
BLOCK_SOFTIRQ,
TASKLET_SOFTIRQ,
SCHED_SOFTIRQ,
HRTIMER_SOFTIRQ,
RCU_SOFTIRQ, /* Preferable RCU should always be the lastsoftirq */
NR_SOFTIRQS
}; 注册函数如下:
open_softirq(HI_SOFTIRQ,tasklet_hi_action);
open_softirq(TIMER_SOFTIRQ,run_timer_softirq);
open_softirq(NET_TX_SOFTIRQ,net_tx_action);
open_softirq(NET_RX_SOFTIRQ,net_rx_action);
open_softirq(BLOCK_SOFTIRQ,blk_done_softirq);
open_softirq(TASKLET_SOFTIRQ,tasklet_action);
open_softirq(SCHED_SOFTIRQ,run_rebalance_domains);
open_softirq(HRTIMER_SOFTIRQ, run_hrtimer_softirq);
在irq中每一个CPU都有个结构体
typedef struct {
unsignedlong __softirq_pending;
} ____cacheline_aligned irq_cpustat_t; 用来保存当前CPU是否有等待的中断处理函数。
在之前的文章可以知道,系统每一次执行do_irq()结束之后,都会调用函数irq_exit(void)。在这个函数中将处理软件中断,
/*
*Exit an interrupt context. Process softirqs if needed and possible:
*/
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();//同时有软件中断存在,则执行。
#ifdef CONFIG_NO_HZ
/*Make sure that timer wheel updates are propagated */
rcu_irq_exit();
if(idle_cpu(smp_processor_id()) && !in_interrupt() &&!need_resched())
tick_nohz_stop_sched_tick(0);
#endif
preempt_enable_no_resched();
}
#ifdef __ARCH_IRQ_EXIT_IRQS_DISABLED
# define invoke_softirq() __do_softirq()
#else
# define invoke_softirq() do_softirq()
#endif
其do_softirq()函数定义如下:函数中有类似的逻辑。
asmlinkage void do_softirq(void)
{
__u32pending;
unsignedlong flags;
if(in_interrupt())
return;
local_irq_save(flags);
pending= local_softirq_pending();//有软件中断挂起,对挂起的断进行执行。
if(pending)
__do_softirq();
local_irq_restore(flags);
}
#endif
在处理软件中,内核定义了一个内核线程来处理不能及时处理的软中断请求。其函数为:
static int ksoftirqd(void * __bind_cpu);
#define MAX_SOFTIRQ_RESTART 10
asmlinkage void __do_softirq(void)
{
---------------
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, entered softirq %td %s %p"
"with preempt_count %08x,"
" exited with %08x?\n", h -softirq_vec,
softirq_to_name[h - softirq_vec],
h->action, prev_count,preempt_count());
preempt_count()= prev_count;
}
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();
lockdep_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
在__do_softirq()函数中,处理挂起的软中断,同来pending位来判断的是否有中断挂起。
入中达到了(max_restart==0)的时候,pending不为0。调用了函数wakeup_softirqd();
内核线程的创建可参考其他文章。
switch(action) {
caseCPU_UP_PREPARE:
caseCPU_UP_PREPARE_FROZEN:
p = kthread_create(ksoftirqd, hcpu,"ksoftirqd/%d", hotcpu);
tasklet
struct tasklet_struct
{
structtasklet_struct *next;
unsignedlong state;
atomic_tcount;
void(*func)(unsigned long);
unsignedlong data;
};
在linux内核中有俩种类型的tasklet,
其tasklet初始化如下:
void tasklet_init(struct tasklet_struct*t,void (*func)(unsigned long), unsigned long data)
{
t->next= NULL;
t->state= 0;
atomic_set(&t->count,0);
t->func= func;
t->data= data;
}
初始化完tasklet之后,将其加入到tasklet队列中。
低优先级tasklet的调度函数如下:
static inline void tasklet_schedule(structtasklet_struct *t)
{
if(!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
void __tasklet_schedule(structtasklet_struct *t)
{
unsignedlong flags;
local_irq_save(flags);
t->next= NULL;
*__get_cpu_var(tasklet_vec).tail= t;
__get_cpu_var(tasklet_vec).tail= &(t->next);
raise_softirq_irqoff(TASKLET_SOFTIRQ);//请求软中断。
local_irq_restore(flags);
}
高优先级tasklet的调度函数如下:
static inline voidtasklet_hi_schedule(struct tasklet_struct *t)
{
if(!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_hi_schedule(t);
}
void __tasklet_hi_schedule(structtasklet_struct *t)
{
unsignedlong flags;
local_irq_save(flags);
t->next= NULL;
*__get_cpu_var(tasklet_hi_vec).tail= t;
__get_cpu_var(tasklet_hi_vec).tail= &(t->next);
raise_softirq_irqoff(HI_SOFTIRQ);//请求软中断。
local_irq_restore(flags);
}
有上面的俩组函数可知,将tasklet放入到不同的tasklet_vec和tasklet_hi_vec队列中。