對於軟中斷,實現還是很完美的。每乙個中斷上半部都對應乙個軟中斷,可以在多核上併發進行。以塊裝置軟中斷為例,初始化如下
blk_dev_init
=>for_each_possible_cpu(i)//每個cpu都部署
init_list_head(&per_cpu(blk_cpu_done, i));
=>open_softirq(block_softirq, blk_done_softirq, null);
=>softirq_vec[nr].data = data;
=>softirq_vec[nr].action = action;
上半部交接
blk_complete_request
=>cpu_list = &__get_cpu_var(blk_cpu_done);
=>list_add_tail(&req->donelist, cpu_list);//每個完成的req都加到佇列裡面
=>raise_softirq_irqoff(block_softirq);//喚醒軟中斷
=>__raise_softirq_irqoff(nr);
=>or_softirq_pending(1ul << (nr)); //置pending位
=>wakeup_softirqd()
=>struct task_struct *tsk = __get_cpu_var(ksoftirqd);
=>wake_up_process(tsk);//喚醒ksoftirqd
軟中斷粉墨登場
ksoftirqd
=>set_current_state(task_interruptible);
=>while (!kthread_should_stop())
preempt_disable();//關閉搶占計數
if (!local_softirq_pending()) //如果沒有pending計數
schedule();//切換出去
preempt_disable();
__set_current_state(task_running);
while (local_softirq_pending())//有pending計數的話
do_softirq();
=>if (local_softirq_pending())
=>curctx = current_thread_info();//切換到軟中斷上下文
irqctx = softirq_ctx[smp_processor_id()];
irqctx->tinfo.task = curctx->task;
irqctx->tinfo.previous_esp = current_stack_pointer;
/* build the stack frame on the softirq stack */
isp = (u32*) ((char*)irqctx + sizeof(*irqctx));
=>call __do_softirq
=>h = softirq_vec;
=>do
rcu_bh_qsctr_inc(cpu);
}h++;
pending >>= 1;
} while (pending);
preempt_enable_no_resched();
cond_resched();
preempt_disable();
preempt_enable();
set_current_state(task_interruptible);
=>__set_current_state(task_running);
軟中斷成本很高,linux也就定義了6種,塊裝置和網口是典型,中斷頻繁觸發,軟中斷隨之頻繁,但是任務本身不重。如果中斷不是超級頻繁,下半部不是超級複雜。那麼,tasklet是個很好的選擇。
tasklet不管有多少個cpu,1種型別只有1個,沒有佇列。以鍵盤中斷為例,定義如下:
declare_tasklet_disabled(keyboard_tasklet, kbd_bh, 0);
鍵盤中斷交接給tasklet
request_irq(ec3104_irq_ser4, ec3104_keyb_interrupt, 0, "keyboard", null);
=>ec3104_keyb_interrupt
=>e5_receive(k);
=>handle_keyboard_event(k->packet[1]);
=>tasklet_schedule(&keyboard_tasklet);
tasklet執行
tasklet_schedule(&keyboard_tasklet);
=>if (!test_and_set_bit(tasklet_state_sched, &t->state))//當tasklet已經處於排程狀態但是還沒有執行時,新來的同型別的tasklet丟棄掉,因為tasklet只有1個全域性變數
__tasklet_schedule(t);
=>t->next = __get_cpu_var(tasklet_vec).list;//將tasklet加入到tasklet裡面,這裡面的tasklet是不同型別tasklet的集合
__get_cpu_var(tasklet_vec).list = t;
=>raise_softirq_irqoff(tasklet_softirq);
=......=>tasklet_action
=>list = __get_cpu_var(tasklet_vec).list;//把佇列裡面的tasklet全部摘除
__get_cpu_var(tasklet_vec).list = null;
=>while (list)//序列執行所有的tasklet
struct tasklet_struct *t = list;
list = list->next;
if (tasklet_trylock(t))
tasklet_trylock
=>return !test_and_set_bit(tasklet_state_run, &(t)->state);//是否處於run狀態
test_and_clear_bit(tasklet_state_sched, &t->state)//取消tasklet的排程狀態,這個時候如果新來乙個同型別的tasklet,則能掛上去
t->func(t->data);//執行tasklet
tasklet_unlock(t);//解除run狀態
continue;//回到while (list)繼續序列執行下乙個tasklet
local_irq_disable();//如果tasklet處於run狀態,則把佇列重新掛回去
t->next = __get_cpu_var(tasklet_vec).list;
__get_cpu_var(tasklet_vec).list = t;
__raise_softirq_irqoff(tasklet_softirq);
local_irq_enable();
由於tasklet只有1個,處於排程態的話只能有1個,所以不需要重入。所以在鍵盤,滑鼠等處理任務不是很繁重的情況下很合適。
如果中斷不是很繁重,但是下半部需要處理的任務很重很繁瑣,不是那麼緊急的話,wq就更合適了。
linux 軟中斷機制分析
linux 核心軟中斷詳解
總結一下linux軟中斷的實現
kernel中斷分析七——tasklet
深入分析tasklet機制
nginx伺服器上軟中斷過高問題如何解決
軟中斷與硬中斷 作者的部落格不錯
中斷管理之下半部軟中斷
linux中斷管理中有個非常重要的設計理念就是上下半部機制 上半部就是硬體中斷管理 中斷設計為上下半部的原因如下 1.硬體中斷處理程式以非同步方式進行 它會打斷其他重要的 執行 因此為了避免被打斷的程式停止時間太長 硬體中斷處理程式必須盡快完成 2.硬體中斷處理程式通常在關閉中斷的情況下執行 關閉中...
學習筆記之中斷下半部
中斷下半部 由於中斷處理力求時間短,所以引出了下半部。tasklet工作在中斷上下文,處理中不允許休眠,而workqueue工作在程序上下文,這是不同點。1,softirq 處理比較快,但是核心級別的機制,需要修改整個核心原始碼,不推薦也不常用 2,tasklet 內部實現實際呼叫了softirq ...
效能測試必備知識(9) 深入理解「軟中斷」
做效能測試的必備知識系列,可以看下面鏈結的文章哦 比如說你訂了乙份外賣,但是不確定外賣什麼時候送到,也沒有別的方法了解外賣的進度,但是,配送員送外賣是不等人的,到了你這兒沒人取的話,就直接走人了 所以你只能苦苦等著,時不時去門口看看外賣送到沒,而不能幹其他事情 不過呢,如果在訂外賣的時候,你就跟配送...