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); <---進入主循環前,先設置任務狀態為TASK_INTERRUPTIBLE
while (!kthread_should_stop()) { <---這個while循環內部處理時,線程可以切出,但仍處與運行隊列
if (!local_softirq_pending()) <---如果不存在需要處理的軟中斷,則線程切出並移出運行隊列
schedule();
__set_current_state(TASK_RUNNING); <---如果有活兒干,則恢復線程的狀態為TASK_RUNNING
while (local_softirq_pending()) { <---這個循環是干活的主循環
preempt_disable(); <---關閉內核搶占,目的是為了保證能連續處理完pending的軟中斷
if (cpu_is_offline((long)__bind_cpu))
goto wait_to_die;
do_softirq(); <---處理軟中斷(轉發、報文收發處理)
preempt_enable();
cond_resched(); <---運行“指定時間片”後本線程強行切出,切出時任務狀態還是TASK_RUNNING
}
set_current_state(TASK_INTERRUPTIBLE); <---設置任務狀態為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)); <--測試TIF_NEED_RESCHED標志是否設置,主要是時鐘中斷調用的scheduler_tick()函數設置本線程的時間片是否到(本線程運行時間是否過長),如果到了,說明線程運行的時間足夠長了,那就歇會兒吧,不要把系統其他線程餓死了,畢竟本線程是禁止了內核搶占的
}
上述軟中斷線程處理得經典框架可以作為有類似處理內核線程的通用框架 。
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;
}