参考:http://blog.youkuaiyun.com/yiyeguzhou100/article/details/49909787
http://blog.youkuaiyun.com/adaptiver/article/details/6177646
http://yaoyang.blog.51cto.com/7657153/1261841
在说软中断前,先说一下preempt_count()
thread_info中的成员preempt_count,它是一个32位的字段,分为几个部分,:
0-7位: 抢占计数器, 最大值255
8-15位: 软中断计数器, 最大值255
16-27位: 硬中断计数器, 最大值4096
28位: PREEMPT_ACTIVE标志
因此,在hardirq.h中定义了几个宏:
#define PREEMPT_BITS 8
#define SOFTIRQ_BITS 8
#define HARDIRQ_BITS 12
#define PREEMPT_SHIFT 0
#define SOFTIRQ_SHIFT (PREEMPT_SHIFT + PREEMPT_BITS)
#define HARDIRQ_SHIFT (SOFTIRQ_SHIFT + SOFTIRQ_BITS)
#define PREEMPT_OFFSET (1UL << PREEMPT_SHIFT)
#define SOFTIRQ_OFFSET (1UL << SOFTIRQ_SHIFT)
#define HARDIRQ_OFFSET (1UL << HARDIRQ_SHIFT)
因此, 函数调用add_preempt_count(HARDIRQ_OFFSET)是增加其中硬中断的计数.
in_interrupt()–>irq_count()–>(preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK | NMI_MASK))
其中SOFTIRQ_MASK是软中断计数器所在位的全部为1的数。掩码就是全为1,如PREEMPT_BITS的掩码为11111111。
如:在中断退出时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();
rcu_irq_exit();
#ifdef CONFIG_NO_HZ
/* Make sure that timer wheel updates are propagated */
if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
tick_nohz_stop_sched_tick(0);
#endif
preempt_enable_no_resched();
}
分析__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)); 软件抢占计数加1,即屏蔽其他的软中断
lockdep_softirq_enter(); 软中断上下文计数加1
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) { //这是针对每大类,如:TASKLET_SOFTIRQ HI_SOFTIRQ等
int prev_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_qs(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(); 软中断上下文计数减1
account_system_vtime(current);
_local_bh_enable();
}