1. 註冊非強制中斷當然是通過open_softirq
例子如下:
void __init init_timers(void){int err = timer_cpu_notify(&timers_nb, (unsigned long)CPU_UP_PREPARE,(void *)(long)smp_processor_id());init_timer_stats();BUG_ON(err == NOTIFY_BAD);register_cpu_notifier(&timers_nb);open_softirq(TIMER_SOFTIRQ, run_timer_softirq);}void open_softirq(int nr, void (*action)(struct softirq_action *)){softirq_vec[nr].action = action;}
非強制中斷TIMER_SOFTIRQ的中斷處理函數為:run_timer_softirq
之所以成為softirq,是因為這些中斷是由硬體中斷來間接觸發的,如何間接觸發的呢:
硬體中斷處理函數-->對非強制中斷的相應位置位-->喚醒ksoftirqd線程-->執行非強制中斷的中斷處理函數
2. 硬體中斷如何通過置位喚醒ksoftirqd線程
timer interrupt handler->
timer_tick->
update_process_times->
run_local_timers->
hrtimer_run_queues()和raise_softirq(TIMER_SOFTIRQ)->
raise_softirq_irqoff->
__raise_softirq_irqoff { or_softirq_pending(1UL << (nr)); }
即(local_softirq_pending() |= (x))
3. 如何執行非強制中斷的action<中斷處理函數>
對於TIMER_SOFTIRQ來說,每次system clock產生中斷時,即一個tick 到來時,在system clock的中斷處理函數中會調用run_local_timers來設定TIMER_SOFTIRQ觸發條件;也就是當前CPU對應的irq_cpustat_t結構體中的__softirq_pending成員的第TIMER_SOFTIRQ個BIT被置為1。 而當這個條件滿足時,ksoftirqd線程(入口函數run_ksoftirqd,cpu_callback:kthread_create(run_ksoftirqd,
hcpu, "ksoftirqd/%d", hotcpu);)會被喚醒,然後按照下面的流程調用TIMER_SOFTIRQ在數組softirq_vec中註冊的action,即run_timer_softirq。
run_ksoftirqd--->do_softirq--->__do_softirq--->softirq_vec[TIMER_SOFTIRQ].action
static int run_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_sched_qs((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;}