linux內核的軟中斷實現教程

軟中斷是內核提供的一種延遲機制,完全由軟件觸發。雖然是延遲機制,實際上,在大多數情況下,它比普通進程能夠得到更快的響應。軟中斷也是內核其他機制的基礎,如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 *))


分享到:


相關文章: