Linux 2.6.11版本的内核软中断线程ksoftirqd代码如下,下面框架可以看出对于,吞吐量与处理延迟两者之间的权衡。
软中断线程处理概括:
1、如果没有活干(没有软中断等待处理)就 schedule()切出,并从运行队列里面删除(由于任务状态已经变成TASK_INTERRUPTIBLE)
2、如果有活儿干,就把当前pending的软中断处理完,处理过程中检查如果本线程运行时间过长,则 schedule()切出(cond_resched()完成这一切),避免其他线程饿死。但切出时任务状态是TASK_RUNNING,不会移出运行队列(线程还有活儿没干完,得赶紧回来),由于移出前,关闭了内核抢占,很快就会重新调度到本线程的。
3、系统调度让其他进程运行一段时间后,本进程重新切换回来运行,重复第2步,一直到该处理得软中断处理完
4、一批软中断处理完后,设置任务状态是TASK_INTERRUPTIBLE,为无软新的软中断过来时切出去并移出运行队列准备。
假如:一次来了8个软中断要处理,需要连续处理完8个软中断,期间可以会切出;如果处理完这组最后一个软中断后正好切出(调用 cond_resched()时里面检查时间片到),然后一段时间后切换回来,发现切出的这段时间有新的软中断等待处理,则有继续处理。
static int ksoftirqd (void * __bind_cpu)
{
set_user_nice(current, 19);
current->flags |= PF_NOFREEZE;
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
if (!local_softirq_pending())
schedule();
__set_current_state(TASK_RUNNING);
while (local_softirq_pending()) {
preempt_disable();
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq();
preempt_enable();
cond_resched();
}
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
wait_to_die:
preempt_enable();
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
schedule();
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}
int __sched cond_resched (void)
{
if (need_resched()) {
__cond_resched();
return 1;
}
return 0;
}
static inline void __cond_resched (void)
{
do {
add_preempt_count(PREEMPT_ACTIVE);
schedule();
sub_preempt_count(PREEMPT_ACTIVE);
} while (need_resched());
}
static inline int need_resched (void)
{
return unlikely(test_thread_flag(TIF_NEED_RESCHED));
}
上述软中断线程处理得经典框架可以作为有类似处理内核线程的通用框架 。
static int kmythread (...)
{
...
set_current_state(TASK_INTERRUPTIBLE);
while (!kthread_should_stop()) {
if (!local_softirq_pending()) /* 这段代码对于通用线程可以删除 */
schedule();
__set_current_state(TASK_RUNNING);
while (/* 有自己的数据处理,如:自己的队列里面有报文需要处理等 */) {
preempt_disable();
/* 干活... */
preempt_enable();
cond_resched();
}
set_current_state(TASK_INTERRUPTIBLE);
}
__set_current_state(TASK_RUNNING);
return 0;
}