Chinaunix

标题: linux软中断 [打印本页]

作者: feiyang10086    时间: 2011-12-27 22:17
标题: linux软中断

linux软中断








在由内核执行的几个任务之间有些不是紧急的,在必要情况下他们可以延迟一段时间。一个中断处理程序的几个中断服务例程之间是串行执行的,并且通常在一个中断的处理程序结束前,不应该再次出现这个中断。相反,可延迟中断可以在开中断的情况下执行。

linux中所谓的可延迟函数,包括软中断和tasklet以及通过中作队列执行的函数(这个以后说),软中断的分配是静态的(即值编译时定义),而tasklet的分配和初始化可以在运行时进行。

软中断

软中断所使用的数据结构定义为



view plaincopy to clipboard
  1. 01.static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  
复制代码
其中softirq_action类型为一个函数指针,从这里也可以看出,软中断的个数是有限的有NR_SOFTIRQS个,具体的定义如下:



view plaincopy to clipboard
  1. 01.enum  
  2. 02.{  
  3. 03.    HI_SOFTIRQ=0,  
  4. 04.    TIMER_SOFTIRQ,  
  5. 05.    NET_TX_SOFTIRQ,  
  6. 06.    NET_RX_SOFTIRQ,  
  7. 07.    BLOCK_SOFTIRQ,  
  8. 08.    BLOCK_IOPOLL_SOFTIRQ,  
  9. 09.    TASKLET_SOFTIRQ,  
  10. 10.    SCHED_SOFTIRQ,  
  11. 11.    HRTIMER_SOFTIRQ,  
  12. 12.    RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */  
  13. 13.  
  14. 14.    NR_SOFTIRQS  
  15. 15.};  
复制代码
可以看出,一共10个软中断。



view plaincopy to clipboard
  1. 01.struct softirq_action  
  2. 02.{  
  3. 03.    void    (*action)(struct softirq_action *);  
  4. 04.};
复制代码
软中断的初始化



view plaincopy to clipboard
  1. 01./*初始化软中断*/  
  2. 02.void open_softirq(int nr, void (*action)(struct softirq_action *))  
  3. 03.{  
  4. 04.    softirq_vec[nr].action = action;  
  5. 05.}  
复制代码
上面函数中,参数nr为softirq_vec[]数组的下标,初始化就是初始化softirq_vec[]数组内容。

初始化了软中断后,要执行,接下来要做的是激活软中断,运用下面函数



view plaincopy to clipboard
  1. 01./*激活软中断*/  
  2. 02.void raise_softirq(unsigned int nr)  
  3. 03.{  
  4. 04.    unsigned long flags;  
  5. 05.    /*保存eflags寄存器IF标志的状态值
  6. 06.    并禁用本地CPU上得中断*/  
  7. 07.    local_irq_save(flags);  
  8. 08.      
  9. 09.    raise_softirq_irqoff(nr);  
  10. 10.    local_irq_restore(flags);  
  11. 11.}  
复制代码
具体的激活工作由raise_softirq_irqoff函数实现



view plaincopy to clipboard
  1. 01./*
  2. 02. * This function must run with irqs disabled!
  3. 03. */  
  4. 04.inline void raise_softirq_irqoff(unsigned int nr)  
  5. 05.{     
  6. 06.    /*把软中断标记为挂起*/  
  7. 07.    __raise_softirq_irqoff(nr);  
  8. 08.  
  9. 09.    /*
  10. 10.     * If we're in an interrupt or softirq, we're done
  11. 11.     * (this also catches softirq-disabled code). We will
  12. 12.     * actually run the softirq once we return from
  13. 13.     * the irq or softirq.
  14. 14.     *
  15. 15.     * Otherwise we wake up ksoftirqd to make sure we
  16. 16.     * schedule the softirq soon.
  17. 17.     */  
  18. 18.    if (!in_interrupt())  
  19. 19.        wakeup_softirqd();/*唤醒本地的内核线程*/  
  20. 20.}  
复制代码
守护线程softirqd就是对软中断的处理



view plaincopy to clipboard
  1. 01.static int ksoftirqd(void * __bind_cpu)  
  2. 02.{  
  3. 03.    /*设置进程状态为可中断*/  
  4. 04.    set_current_state(TASK_INTERRUPTIBLE);  
  5. 05.  
  6. 06.    while (!kthread_should_stop()) {/*不应该马上返回*/  
  7. 07.        preempt_disable();  
  8. 08.        /*实现软中断中一个关键数据结构是每个
  9. 09.        CPU都有的32位掩码(描述挂起的软中断),
  10. 10.        他存放在irq_cpustat_t数据结构的__softirq_pending
  11. 11.        字段中。为了获取或设置位掩码的值,
  12. 12.        内核使用宏local_softirq_pending,他选择cpu的
  13. 13.        软中断为掩码*/  
  14. 14.        if (!local_softirq_pending()) {/*位掩码为0,标示没有软中断*/  
  15. 15.            preempt_enable_no_resched();  
  16. 16.            schedule();  
  17. 17.            preempt_disable();  
  18. 18.        }  
  19. 19.  
  20. 20.        __set_current_state(TASK_RUNNING);  
  21. 21.  
  22. 22.        while (local_softirq_pending()) {  
  23. 23.            /* Preempt disable stops cpu going offline.
  24. 24.               If already offline, we'll be on wrong CPU:
  25. 25.               don't process */  
  26. 26.            if (cpu_is_offline((long)__bind_cpu))  
  27. 27.                goto wait_to_die;  
  28. 28.            do_softirq();/*调用软中断处理函数*/  
  29. 29.            preempt_enable_no_resched();  
  30. 30.            cond_resched();  
  31. 31.            preempt_disable();  
  32. 32.            rcu_sched_qs((long)__bind_cpu);  
  33. 33.        }  
  34. 34.        preempt_enable();  
  35. 35.        set_current_state(TASK_INTERRUPTIBLE);  
  36. 36.    }  
  37. 37.    __set_current_state(TASK_RUNNING);  
  38. 38.    return 0;  
  39. 39.  
  40. 40.wait_to_die:  
  41. 41.    preempt_enable();  
  42. 42.    /* Wait for kthread_stop */  
  43. 43.    set_current_state(TASK_INTERRUPTIBLE);  
  44. 44.    while (!kthread_should_stop()) {  
  45. 45.        schedule();  
  46. 46.        set_current_state(TASK_INTERRUPTIBLE);  
  47. 47.    }  
  48. 48.    __set_current_state(TASK_RUNNING);  
  49. 49.    return 0;  
  50. 50.}  
复制代码
下面是软中断的执行



view plaincopy to clipboard
  1. 01.<pre class="cpp" name="code">/*如果在这样的一个检查点(local_softirq_pending()不为0)
  2. 02.检测到挂起的软中断,内核调用下面函数处理*/  
  3. 03.asmlinkage void do_softirq(void)  
  4. 04.{  
  5. 05.    __u32 pending;  
  6. 06.    unsigned long flags;  
  7. 07.  
  8. 08.    if (in_interrupt())  
  9. 09.        return;  
  10. 10.  
  11. 11.    local_irq_save(flags);  
  12. 12.  
  13. 13.    pending = local_softirq_pending();  
  14. 14.  
  15. 15.    if (pending)  
  16. 16.        __do_softirq();  
  17. 17.  
  18. 18.    local_irq_restore(flags);  
  19. 19.}  
复制代码
具体由__do_softirq函数实现



view plaincopy to clipboard
  1. 01./*读取本地CPU的软中断掩码并执行与每个设置位
  2. 02.相关的可延迟函数,__do_softirq只做固定次数的循环
  3. 03.然后就返回。如果还有其余挂起的软中断,那么
  4. 04.内核线程ksofirqd将会在预期的时间内处理他们*/  
  5. 05.asmlinkage void __do_softirq(void)  
  6. 06.{  
  7. 07.    struct softirq_action *h;  
  8. 08.    __u32 pending;  
  9. 09.    /*把循环计数器的值初始化为10*/  
  10. 10.    int max_restart = MAX_SOFTIRQ_RESTART;  
  11. 11.    int cpu;  
  12. 12.    /*把本地CPU(被local_softirq_pending选中的)软件中断的
  13. 13.    位掩码复制到局部变量pending中*/  
  14. 14.    pending = local_softirq_pending();  
  15. 15.    account_system_vtime(current);  
  16. 16.    /*增加软中断计数器的值*/  
  17. 17.    __local_bh_disable((unsigned long)__builtin_return_address(0));  
  18. 18.    lockdep_softirq_enter();  
  19. 19.  
  20. 20.    cpu = smp_processor_id();  
  21. 21.restart:  
  22. 22.    /* Reset the pending bitmask before enabling irqs */  
  23. 23.    set_softirq_pending(0);/*清除本地CPU的软中断位图,
  24. 24.    以便可以激活新的软中断*/  
  25. 25.  
  26. 26.    /*激活本地中断*/  
  27. 27.    local_irq_enable();  
  28. 28.  
  29. 29.    h = softirq_vec;  
  30. 30.  
  31. 31.    do {/*根据pending每一位的的设置,执行对应的软中断
  32. 32.        处理函数*/  
  33. 33.        if (pending & 1) {  
  34. 34.            int prev_count = preempt_count();  
  35. 35.            kstat_incr_softirqs_this_cpu(h - softirq_vec);  
  36. 36.  
  37. 37.            trace_softirq_entry(h, softirq_vec);  
  38. 38.            h->action(h);/*执行注册的具体的软中断函数*/  
  39. 39.            trace_softirq_exit(h, softirq_vec);  
  40. 40.            if (unlikely(prev_count != preempt_count())) {  
  41. 41.                printk(KERN_ERR "huh, entered softirq %td %s %p"  
  42. 42.                       "with preempt_count %08x,"  
  43. 43.                       " exited with %08x?\n", h - softirq_vec,  
  44. 44.                       softirq_to_name[h - softirq_vec],  
  45. 45.                       h->action, prev_count, preempt_count());  
  46. 46.                preempt_count() = prev_count;  
  47. 47.            }  
  48. 48.  
  49. 49.            rcu_bh_qs(cpu);  
  50. 50.        }  
  51. 51.        h++;  
  52. 52.        pending >>= 1;  
  53. 53.    } while (pending);  
  54. 54.  
  55. 55.    local_irq_disable();  
  56. 56.  
  57. 57.    pending = local_softirq_pending();  
  58. 58.    if (pending && --max_restart)  
  59. 59.        goto restart;  
  60. 60.  
  61. 61.    if (pending)/*如果还有挂起的软中断,唤醒内核线程
  62. 62.            来处理本地CPU的软中断*/  
  63. 63.        wakeup_softirqd();  
  64. 64.  
  65. 65.    lockdep_softirq_exit();  
  66. 66.  
  67. 67.    account_system_vtime(current);  
  68. 68.    _local_bh_enable();/*软中断计数器-1,因而重新激活可延迟函数*/  
  69. 69.}  
复制代码
到此,linux内核软中断的大致执行和实现基本上分析完了,中间有很多地方没有注释的,主要是考虑到需要别的实现机制以及有的比较易懂。能够自己看懂。

作者: 芯忻相依    时间: 2011-12-27 22:18
谢谢分享
作者: bsdc    时间: 2014-06-11 10:45
大约看懂了

中断上下文:就是在有时间要求的硬件中断中把可以延时、可以在开中断的情况下执行的下半部放入softirq中去做,__do_softirq处理10个软中断请求,超过后把其余请求交给ksoftirq线程;

内核上下文:而ksoftirq做的工作也就是调用__do_softirq,如此反复,知道任务完成。
作者: hexilanlan    时间: 2014-06-13 11:27
额。围观。。。。




欢迎光临 Chinaunix (http://bbs.chinaunix.net/) Powered by Discuz! X3.2