博客列表 >linux内核的软中断实现教程

linux内核的软中断实现教程

Coco
Coco原创
2021年12月11日 14:31:001043浏览

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

  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. 到这里已经打开了本地中断,下面QQ靓号买号平台在软中断处理执行过程中可能会被硬件中断抢占

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

声明:本文内容转载自脚本之家,由网友自发贡献,版权归原作者所有,如您发现涉嫌抄袭侵权,请联系admin@php.cn 核实处理。
全部评论
文明上网理性发言,请遵守新闻评论服务协议