linux内核的软中断实现教程

zhongcanw 2018-06-21

软中断是内核提供的一种延迟机制,完全由软件触发。虽然是延迟机制,实际上,在大多数情况下,它比普通进程能够得到更快的响应。软中断也是内核其他机制的基础,如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",

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 *))

相关推荐