鏈接:http://bbs.chinaunix.net/thread-4125504-1-1.html
原問:
看博客有寫到:一個軟中斷不會去搶佔另一個軟中斷。
個人理解如下:《Linux內核設計與實現》中提到,如果軟中斷存在共享數據,那麼需要進行加鎖保護,因爲同類型的軟中斷可以同時在不同的CPU下運行。所以,一個軟中斷不會去搶佔另一個軟中斷,這句話的是在一個CPU上吧?
以下內容爲個人猜想,不對的地方還請指正。假設硬件中斷頻繁發生,那麼就會擠壓了多個下半部(tasklet)在等待處理,這些tasklet應該是以隊列的形式存放起來的。空閒的CPU從隊列中取出一個tasklet進行處理,所以所有CPU都在處理軟中斷。 假設在一個CPU上,正在運行軟中斷A,此時來了一箇中斷,該中斷處理也分爲硬中斷B和軟中斷B,硬中斷B肯定可以搶佔CPU運行。 那麼接下來,執行軟中斷A還是軟中斷B呢? 是軟中斷A運行完以後,立即運行B,還是會放入隊列當中啊?
6樓回覆:
如果你這裏的軟中斷時指softirq的話,那麼:
查看了一下代碼,流程應該是這樣:1. 當某CPU正在run softirq A,這時來了一個硬件中斷,硬件中斷打斷軟件中斷;
2. 硬件中斷在上半部raise了軟中斷B的pending標誌;
3. 硬件中斷運行完畢,call irq_exit :
- void irq_exit(void)
- {
- account_system_vtime(current);
- trace_hardirq_exit();
- sub_preempt_count(IRQ_EXIT_OFFSET);
- if (!in_interrupt() && local_softirq_pending())
- invoke_softirq();
- rcu_irq_exit();
- #ifdef CONFIG_NO_HZ
- /* Make sure that timer wheel updates are propagated */
- if (idle_cpu(smp_processor_id()) && !in_interrupt() && !need_resched())
- tick_nohz_stop_sched_tick(0);
- #endif
- preempt_enable_no_resched();
- }
if (!in_interrupt() && local_softirq_pending())
invoke_softirq();
是說如果現在的環境本身就是在中斷中(軟中斷也是在中斷環境中),那麼!in_interrupt()就會返回false,那麼就不會運行invoke_softirq()
5. 硬件中斷繼續返回到被打斷的地方(即先前的softirq A的處理中),kernel繼續運行softirq A。
請注意在step 2中kernel已經raise了softirq B的pending位,所以在函數__do_softirq中,會重新check到softirq B的bit,從而運行softirq B的action
- 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();
- kstat_incr_softirqs_this_cpu(h - softirq_vec);
- trace_softirq_entry(h, softirq_vec);
- h->action(h);
- trace_softirq_exit(h, softirq_vec);
- if (unlikely(prev_count != preempt_count())) {
- printk(KERN_ERR "huh, entered softirq %td %s %p"
- "with preempt_count %08x,"
- " exited with %08x?\n", h - softirq_vec,
- softirq_to_name[h - softirq_vec],
- h->action, prev_count, preempt_count());
- preempt_count() = prev_count;
- }
- rcu_bh_qs(cpu);
- }
- h++;
- pending >>= 1;
- } while (pending);
- local_irq_disable();
- pending = local_softirq_pending();
- if (pending && --max_restart)
- goto restart;