- 论坛徽章:
- 0
|
linux软中断
在由内核执行的几个任务之间有些不是紧急的,在必要情况下他们可以延迟一段时间。一个中断处理程序的几个中断服务例程之间是串行执行的,并且通常在一个中断的处理程序结束前,不应该再次出现这个中断。相反,可延迟中断可以在开中断的情况下执行。
linux中所谓的可延迟函数,包括软中断和tasklet以及通过中作队列执行的函数(这个以后说),软中断的分配是静态的(即值编译时定义),而tasklet的分配和初始化可以在运行时进行。
软中断
软中断所使用的数据结构定义为
view plaincopy to clipboard- 01.static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;
复制代码 其中softirq_action类型为一个函数指针,从这里也可以看出,软中断的个数是有限的有NR_SOFTIRQS个,具体的定义如下:
view plaincopy to clipboard- 01.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 clipboard- 01.struct softirq_action
- 02.{
- 03. void (*action)(struct softirq_action *);
- 04.};
复制代码 软中断的初始化
view plaincopy to clipboard- 01./*初始化软中断*/
- 02.void open_softirq(int nr, void (*action)(struct softirq_action *))
- 03.{
- 04. softirq_vec[nr].action = action;
- 05.}
复制代码 上面函数中,参数nr为softirq_vec[]数组的下标,初始化就是初始化softirq_vec[]数组内容。
初始化了软中断后,要执行,接下来要做的是激活软中断,运用下面函数
view plaincopy to clipboard- 01./*激活软中断*/
- 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 clipboard- 01./*
- 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 clipboard- 01.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 clipboard- 01.<pre class="cpp" name="code">/*如果在这样的一个检查点(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 clipboard- 01./*读取本地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[h - softirq_vec],
- 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内核软中断的大致执行和实现基本上分析完了,中间有很多地方没有注释的,主要是考虑到需要别的实现机制以及有的比较易懂。能够自己看懂。
|
|