linux軟中斷
linux軟中斷
在由內核執行的幾個任務之間有些不是緊急的,在必要情況下他們可以延遲一段時間。一個中斷處理程序的幾個中斷服務常式之間是串列執行的,並且通常在一個中斷的處理程序結束前,不應該再次出現這個中斷。相反,可延遲中斷可以在開中斷的情況下執行。
linux中所謂的可延遲函數,包括軟中斷和tasklet以及通過中作隊列執行的函數(這個以後說),軟中斷的分配是靜態的(即值編譯時定義),而tasklet的分配和初始化可以在運行時進行。
軟中斷
軟中斷所使用的數據結構定義為
view plaincopy to clipboard01.static struct softirq_action softirq_vec __cacheline_aligned_in_smp; 其中softirq_action類型為一個函數指針,從這裡也可以看出,軟中斷的個數是有限的有NR_SOFTIRQS個,具體的定義如下:
view plaincopy to clipboard01.enum
02.{
03. HI_SOFTIRQ=0,
04. TIMER_SOFTIRQ,
05. NET_TX_SOFTIRQ,
06. NET_RX_SOFTIRQ,
07. BLOCK_SOFTIRQ,
08. BLOCK_IOPOLL_SOFTIRQ,
09. TASKLET_SOFTIRQ,
10. SCHED_SOFTIRQ,
11. HRTIMER_SOFTIRQ,
12. RCU_SOFTIRQ, /* Preferable RCU should always be the last softirq */
13.
14. NR_SOFTIRQS
15.}; 可以看出,一共10個軟中斷。
view plaincopy to clipboard01.struct softirq_action
02.{
03. void (*action)(struct softirq_action *);
04.}; 軟中斷的初始化
view plaincopy to clipboard01./*初始化軟中斷*/
02.void open_softirq(int nr, void (*action)(struct softirq_action *))
03.{
04. softirq_vec.action = action;
05.} 上面函數中,參數nr為softirq_vec[]數組的下標,初始化就是初始化softirq_vec[]數組內容。
初始化了軟中斷後,要執行,接下來要做的是激活軟中斷,運用下面函數
view plaincopy to clipboard01./*激活軟中斷*/
02.void raise_softirq(unsigned int nr)
03.{
04. unsigned long flags;
05. /*保存eflags寄存器IF標誌的狀態值
06. 並禁用本地CPU上得中斷*/
07. local_irq_save(flags);
08.
09. raise_softirq_irqoff(nr);
10. local_irq_restore(flags);
11.} 具體的激活工作由raise_softirq_irqoff函數實現
view plaincopy to clipboard01./*
02. * This function must run with irqs disabled!
03. */
04.inline void raise_softirq_irqoff(unsigned int nr)
05.{
06. /*把軟中斷標記為掛起*/
07. __raise_softirq_irqoff(nr);
08.
09. /*
10. * If we're in an interrupt or softirq, we're done
11. * (this also catches softirq-disabled code). We will
12. * actually run the softirq once we return from
13. * the irq or softirq.
14. *
15. * Otherwise we wake up ksoftirqd to make sure we
16. * schedule the softirq soon.
17. */
18. if (!in_interrupt())
19. wakeup_softirqd();/*喚醒本地的內核線程*/
20.}
守護線程softirqd就是對軟中斷的處理
view plaincopy to clipboard01.static int ksoftirqd(void * __bind_cpu)
02.{
03. /*設置進程狀態為可中斷*/
04. set_current_state(TASK_INTERRUPTIBLE);
05.
06. while (!kthread_should_stop()) {/*不應該馬上返回*/
07. preempt_disable();
08. /*實現軟中斷中一個關鍵數據結構是每個
09. CPU都有的32位掩碼(描述掛起的軟中斷),
10. 他存放在irq_cpustat_t數據結構的__softirq_pending
11. 欄位中。為了獲取或設置位掩碼的值,
12. 內核使用宏local_softirq_pending,他選擇cpu的
13. 軟中斷為掩碼*/
14. if (!local_softirq_pending()) {/*位掩碼為0,標示沒有軟中斷*/
15. preempt_enable_no_resched();
16. schedule();
17. preempt_disable();
18. }
19.
20. __set_current_state(TASK_RUNNING);
21.
22. while (local_softirq_pending()) {
23. /* Preempt disable stops cpu going offline.
24. If already offline, we'll be on wrong CPU:
25. don't process */
26. if (cpu_is_offline((long)__bind_cpu))
27. goto wait_to_die;
28. do_softirq();/*調用軟中斷處理函數*/
29. preempt_enable_no_resched();
30. cond_resched();
31. preempt_disable();
32. rcu_sched_qs((long)__bind_cpu);
33. }
34. preempt_enable();
35. set_current_state(TASK_INTERRUPTIBLE);
36. }
37. __set_current_state(TASK_RUNNING);
38. return 0;
39.
40.wait_to_die:
41. preempt_enable();
42. /* Wait for kthread_stop */
43. set_current_state(TASK_INTERRUPTIBLE);
44. while (!kthread_should_stop()) {
45. schedule();
46. set_current_state(TASK_INTERRUPTIBLE);
47. }
48. __set_current_state(TASK_RUNNING);
49. return 0;
50.} 下面是軟中斷的執行
view plaincopy to clipboard01.
/*如果在這樣的一個檢查點(local_softirq_pending()不為0)
02.檢測到掛起的軟中斷,內核調用下面函數處理*/
03.asmlinkage void do_softirq(void)
04.{
05. __u32 pending;
06. unsigned long flags;
07.
08. if (in_interrupt())
09. return;
10.
11. local_irq_save(flags);
12.
13. pending = local_softirq_pending();
14.
15. if (pending)
16. __do_softirq();
17.
18. local_irq_restore(flags);
19.}
具體由__do_softirq函數實現
view plaincopy to clipboard01./*讀取本地CPU的軟中斷掩碼並執行與每個設置位
02.相關的可延遲函數,__do_softirq只做固定次數的循環
03.然後就返回。如果還有其餘掛起的軟中斷,那麼
04.內核線程ksofirqd將會在預期的時間內處理他們*/
05.asmlinkage void __do_softirq(void)
06.{
07. struct softirq_action *h;
08. __u32 pending;
09. /*把循環計數器的值初始化為10*/
10. int max_restart = MAX_SOFTIRQ_RESTART;
11. int cpu;
12. /*把本地CPU(被local_softirq_pending選中的)軟體中斷的
13. 位掩碼複製到局部變數pending中*/
14. pending = local_softirq_pending();
15. account_system_vtime(current);
16. /*增加軟中斷計數器的值*/
17. __local_bh_disable((unsigned long)__builtin_return_address(0));
18. lockdep_softirq_enter();
19.
20. cpu = smp_processor_id();
21.restart:
22. /* Reset the pending bitmask before enabling irqs */
23. set_softirq_pending(0);/*清除本地CPU的軟中斷點陣圖,
24. 以便可以激活新的軟中斷*/
25.
26. /*激活本地中斷*/
27. local_irq_enable();
28.
29. h = softirq_vec;
30.
31. do {/*根據pending每一位的的設置,執行對應的軟中斷
32. 處理函數*/
33. if (pending & 1) {
34. int prev_count = preempt_count();
35. kstat_incr_softirqs_this_cpu(h - softirq_vec);
36.
37. trace_softirq_entry(h, softirq_vec);
38. h->action(h);/*執行註冊的具體的軟中斷函數*/
39. trace_softirq_exit(h, softirq_vec);
40. if (unlikely(prev_count != preempt_count())) {
41. printk(KERN_ERR "huh, entered softirq %td %s %p"
42. "with preempt_count %08x,"
43. " exited with %08x?\n", h - softirq_vec,
44. softirq_to_name,
45. h->action, prev_count, preempt_count());
46. preempt_count() = prev_count;
47. }
48.
49. rcu_bh_qs(cpu);
50. }
51. h++;
52. pending >>= 1;
53. } while (pending);
54.
55. local_irq_disable();
56.
57. pending = local_softirq_pending();
58. if (pending && --max_restart)
59. goto restart;
60.
61. if (pending)/*如果還有掛起的軟中斷,喚醒內核線程
62. 來處理本地CPU的軟中斷*/
63. wakeup_softirqd();
64.
65. lockdep_softirq_exit();
66.
67. account_system_vtime(current);
68. _local_bh_enable();/*軟中斷計數器-1,因而重新激活可延遲函數*/
69.} 到此,linux內核軟中斷的大致執行和實現基本上分析完了,中間有很多地方沒有註釋的,主要是考慮到需要別的實現機制以及有的比較易懂。能夠自己看懂。
《解決方案》
謝謝分享