軟中斷是內核提供的一種延遲機制,完全由軟件觸發。雖然是延遲機制,實際上,在大多數情況下,它比普通進程能夠得到更快的響應。軟中斷也是內核其他機制的基礎,如tasklet、高分辨率timer等。
軟中斷資料有限,目前內核中實現了10中類型的軟中斷
1. enum
2. {
3. HI_SOFTIRQ=0,
4. TIMER_SOFTIRQ,
5. NET_TX_SOFTIRQ,
6. NET_RX_SOFTIRQ,
7. BLOCK_SOFTIRQ,
8. BLOCK_IOPOLL_SOFTIRQ,
9. TASKLET_SOFTIRQ,
10. SCHED_SOFTIRQ,
11. HRTIMER_SOFTIRQ,
12. RCU_SOFTIRQ,/*PreferableRCUshouldalwaysbethelastsoftirq*/
13.
14. NR_SOFTIRQS
15. };
內核開發者不建議擅自增加軟中斷數量,如果需要新的軟中斷,儘可能它實現為基於tasklet形式
1、ksoftirqd
early_initcall(spawn_ksoftirqd);---初始化的時候調用spawn_ksoftirqd函數。
smpboot_register_percpu_thread(&softirq_threads)---熱插拔階段為每個percpu上創建一個ksoftirqd守護進程
1.DEFINE_PER_CPU(structtask_struct*,ksoftirqd);
2.
3. staticstructsmp_hotplug_threadsoftirq_threads={
4. .store=&ksoftirqd,
5. .thread_should_run=ksoftirqd_should_run,/*判斷是否應該運行處理軟中斷*/
6. .thread_fn=run_ksoftirqd,/*運行處理軟中斷*/
7. .thread_comm="ksoftirqd/%u",
8. };
9.
10. staticvoidrun_ksoftirqd(unsignedintcpu)
11. {
12. /*關閉本地cpu中斷*/
13. local_irq_disable();
14. if(local_softirq_pending()){/*檢查本地cpu上是否有軟中斷掛起*/
15. /*
16. *Wecansafelyrunsoftirqoninlinestack,aswearenotdeep
17. *inthetaskstackhere.
18. */
19. __do_softirq();/*處理軟中斷*/
20. local_irq_enable();/*使能本地cpu中斷*/
21. cond_resched();/*有條件的重新調度*/
22.
23. preempt_disable();
24. rcu_note_context_switch(cpu);
25. preempt_enable();
26.
27. return;
28. }
29. local_irq_enable();
30. }
2、結構
Irq_cpustat_t,多個軟中斷可以同時在多個cpu運行,就算是同一個軟中斷,也有可能同時在多個cpu上運行。內核為每個cpu都管理著一個待決軟中斷pedding,他就是Irq_cpustat_t
1. typedefstruct{
2. unsignedint__softirq_pending;
3. }____cacheline_alignedirq_cpustat_t;
4.
5.irq_cpustat_tirq_stat[NR_CPUS]____cacheline_aligned;
1. structsoftirq_action
2. {
3. void(*action)(structsoftirq_action*);
4. };
5. staticstructsoftirq_actionsoftirq_vec[NR_SOFTIRQS]__cacheline_aligned_in_smp;
__softirq_pending中每個bit,對應某一個軟中斷,某個Bit被置位,說明有相應的軟總段等待處理。因此最多隻能定義32個軟中斷類型。
3、軟中斷觸發
想觸發軟中斷,只需要調用raise_softirq即可,它的實現簡單先關閉本地cpu中斷,然後調用raise_softirq_irqoff,再打開本地cpu中斷。
1. voidraise_softirq(unsignedintnr)
2. {
3. unsignedlongflags;
4.
5. local_irq_save(flags);
6. raise_softirq_irqoff(nr);
7. local_irq_restore(flags);
8. }
再來看raise_softirq_irqoff
1. inlinevoidraise_softirq_irqoff(unsignedintnr)
2. {
3. __raise_softirq_irqoff(nr);
4.
5. ......
6. if(!in_interrupt())
7. wakeup_softirqd();
8. }
先通過__raise_softirq_irqoff設置cpu的軟中斷pending標誌位(irq_stat(NR_CPUS)),然後通過in_interrupt判斷是否在中斷上下文中,如果不成立,則喚醒軟中斷守護進程,在守護進程中執行軟中斷的回調函數。
4、軟中斷的執行
軟中斷有兩種執行方式,一種是在中斷調用結束時,一種是在ksoftirqd守護進程中
1. /*
2. *Exitaninterruptcontext.Processsoftirqsifneededandpossible:
3. */
4. voidirq_exit(void)
5. {
6. #ifndef__ARCH_IRQ_EXIT_IRQS_DISABLED
7. local_irq_disable();
8. #else
9. WARN_ON_ONCE(!irqs_disabled());
10. #endif
11.
12. account_irq_exit_time(current);
13. preempt_count_sub(HARDIRQ_OFFSET);
14. /*
15. 在中斷髮生嵌套時,通過in_interrupt能確保在最外層的中斷Irq_exit階段
16. invoke_softirq才會被調用
17. */
18. if(!in_interrupt()&&local_softirq_pending())
19. invoke_softirq();
20.
21. tick_irq_exit();
22. rcu_irq_exit();
23. trace_hardirq_exit();/*mustbelast!*/
24. }
代碼最終都會進入到__do_softirq中,執行軟中斷的重點都在該函數中。
1. asmlinkagevoid__do_softirq(void)
2. {
3. unsignedlongend=jiffies+MAX_SOFTIRQ_TIME;
4. unsignedlongold_flags=current->flags;
5. intmax_restart=MAX_SOFTIRQ_RESTART;
6. structsoftirq_action*h;
7. boolin_hardirq;
8. __u32pending;
9. intsoftirq_bit;
10. intcpu;
11.
12. /*
13. *MaskoutPF_MEMALLOCscurrenttaskcontextisborrowedforthe
14. *softirq.AsoftirqhandledsuchasnetworkRXmightsetPF_MEMALLOC
15. *againifthesocketisrelatedtoswap
16. */
17. current->flags&=~PF_MEMALLOC;
18. /*
19. 複製軟中斷掩碼到局部變量,這是必要的
20. 因為local_softirq_pending中的值在開中斷後將不再可靠,必須先保存
21. */
22. pending=local_softirq_pending();
23. account_irq_enter_time(current);
24.
25. /*
26. 標誌下面的代碼正在處理softirq
27. */
28. __local_bh_disable_ip(_RET_IP_,SOFTIRQ_OFFSET);
29. in_hardirq=lockdep_softirq_start();
30.
31. cpu=smp_processor_id();
32. restart:
33. /*Resetthependingbitmaskbeforeenablingirqs*/
34. set_softirq_pending(0);/*清空pending*/
35.
36. local_irq_enable();/*打開本地中斷*/
37.
38. /*
39. 到這裡已經打開了本地中斷,下面在軟中斷處理執行過程中可能會被硬件中斷搶佔
40. */
41. /*
42. 根據軟中斷標誌位處理軟中斷
43. */
44. /* softirq_vec 存放action的結構體*/
45. h=softirq_vec;
46.
47. while((softirq_bit=ffs(pending))){
48. unsignedintvec_nr;
49. intprev_count;
50.
51. h+=softirq_bit-1;
52.
53. vec_nr=h-softirq_vec;
54. prev_count=preempt_count();
55.
56. kstat_incr_softirqs_this_cpu(vec_nr);
57.
58. trace_softirq_entry(vec_nr);
59. h->action(h);
60. trace_softirq_exit(vec_nr);
61. if(unlikely(prev_count!=preempt_count())){
62. pr_err("huh,enteredsoftirq%u%s%pwithpreempt_count%08x,exitedwith%08x\n",
63. vec_nr,softirq_to_name[vec_nr],h->action,
64. prev_count,preempt_count());
65. preempt_count_set(prev_count);
66. }
67. rcu_bh_qs(cpu);
68. h++;
69. pending>>=softirq_bit;
70. }
71.
72. /*關掉本地中斷*/
73. local_irq_disable();
74. /*
75. 由於前面有打開過本地中斷,因此這次可能會有新的軟中斷未處理,再檢查處理,
76. */
77. pending=local_softirq_pending();
78.
if(pending){79. if(time_before(jiffies,end)&&!need_resched()&&
80. --max_restart)
81. gotorestart;
82.
83. wakeup_softirqd();
84. }
85.
86.
87. lockdep_softirq_end(in_hardirq);
88. account_irq_exit_time(current);
89. __local_bh_enable(SOFTIRQ_OFFSET);
90. WARN_ON_ONCE(in_interrupt());
91. tsk_restore_flags(current,old_flags,PF_MEMALLOC);
92. }
5、軟中斷註冊
void open_softirq(int nr,void (*action)(struct softirq_action *))
閱讀更多 IT生涯 的文章