前面的博文中, 大致分散介绍了一些中断相关的东西, 但是对软中断的部分没有仔细介绍, 在这里统一总结一下.
中断上半部的处理,汇编到request_irq的handle之间的过程.
http://blog.csdn.net/jackjones_008/article/details/42387241
MIPS平台的一点记录
http://blog.csdn.net/jackjones_008/article/details/41945909
tasklet/workqueue的介绍在下面这篇博文中有比较详细的介绍.
http://blog.csdn.net/jackjones_008/article/details/42295411
也没有pending的本地softirq, 则会显示的调用do_softirq 去处理softirq. 在处理softirq的过程中, 是会屏蔽硬件中断的.
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);
}
在__do_softirq 里面, 在得到pending的softirq后, 又会打开硬件中断. 然后会去依次处理pending 的softirq.
这个依次, 其实就是根据enum中的定义从 HI_SOFTIRQ 执行到 RCU_SOFTIRQ.
再多啰嗦一下, 为什么会有pending的softirq呢, 因为代码有些地方去调用了 __raise_softirq_irqoff .
我们还可以注意到, __do_softirq 里面定义了一个max_restart, 这个值是10, 是系统的一个折中, 处理软中断, 但是不能过度的影响其他进程的执行.
完成不了的, 可以交给 ksoftirqd 这个内核thread去做.
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) {
int prev_count = preempt_count();
h->action(h);
if (unlikely(prev_count != preempt_count())) {
printk(KERN_ERR "huh, entered softirq %td %p"
"with preempt_count %08x,"
" exited with %08x?\n", 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();
trace_softirq_exit();
account_system_vtime(current);
_local_bh_enable();
}
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 last softirq */
NR_SOFTIRQS
};
接上, restart 中处理完后, 还有pending的softirq的话, 就交给内核进程 ksoftirqd 去做. 这个内核进程也会显式的调用 do_softirq 去处理.
static int ksoftirqd(void * __bind_cpu)
{
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
preempt_disable();
if (!local_softirq_pending()) {
preempt_enable_no_resched();
schedule();
preempt_disable();
}
__set_current_state(TASK_RUNNING);
while (local_softirq_pending()) {
/* Preempt disable stops cpu going offline.
If already offline, we'll be on wrong CPU:
don't process */
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq();
preempt_enable_no_resched();
cond_resched();
preempt_disable();
rcu_qsctr_inc((long)__bind_cpu);
}
preempt_enable();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
wait_to_die:
preempt_enable();
/* Wait for kthread_stop */
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}
这时, 又不可避免的回到了中断上半部和下半部的话题, 上半部我们可以理解为一直执行到 request_irq 中注册的那个 handle 为止. 而softirq 开始的处理都是属于下半部的处理.
在下半部的处理中, 根据睡眠与否可以使用tasklet和workqueue的机制.
其实tasklet也是属于softirq的一种, 而且不可睡眠. workqueue的机制则是交给了内核thread去执行, 可以允许睡眠.