linux软中断

来源:互联网 发布:nginx 下载 linux 编辑:程序博客网 时间:2024/05/22 05:36

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

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

软中断

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

[cpp] view plaincopyprint?
  1. static struct softirq_action softirq_vec[NR_SOFTIRQS] __cacheline_aligned_in_smp;  

其中softirq_action类型为一个函数指针,从这里也可以看出,软中断的个数是有限的有NR_SOFTIRQS个,具体的定义如下:

[cpp] view plaincopyprint?
  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,    /* Preferable RCU should always be the last softirq */  
  13.   
  14.     NR_SOFTIRQS  
  15. };  

可以看出,一共10个软中断。

[cpp] view plaincopyprint?
  1. struct softirq_action  
  2. {  
  3.     void    (*action)(struct softirq_action *);  
  4. };  

软中断的初始化

[cpp] view plaincopyprint?
  1. /*初始化软中断*/  
  2. void open_softirq(int nr, void (*action)(struct softirq_action *))  
  3. {  
  4.     softirq_vec[nr].action = action;  
  5. }  

上面函数中,参数nr为softirq_vec[]数组的下标,初始化就是初始化softirq_vec[]数组内容。

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

[cpp] view plaincopyprint?
  1. /*激活软中断*/  
  2. void raise_softirq(unsigned int nr)  
  3. {  
  4.     unsigned long flags;  
  5.     /*保存eflags寄存器IF标志的状态值 
  6.     并禁用本地CPU上得中断*/  
  7.     local_irq_save(flags);  
  8.       
  9.     raise_softirq_irqoff(nr);  
  10.     local_irq_restore(flags);  
  11. }  

具体的激活工作由raise_softirq_irqoff函数实现

[cpp] view plaincopyprint?
  1. /* 
  2.  * This function must run with irqs disabled! 
  3.  */  
  4. inline void raise_softirq_irqoff(unsigned int nr)  
  5. {     
  6.     /*把软中断标记为挂起*/  
  7.     __raise_softirq_irqoff(nr);  
  8.   
  9.     /* 
  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就是对软中断的处理

[cpp] view plaincopyprint?
  1. static int ksoftirqd(void * __bind_cpu)  
  2. {  
  3.     /*设置进程状态为可中断*/  
  4.     set_current_state(TASK_INTERRUPTIBLE);  
  5.   
  6.     while (!kthread_should_stop()) {/*不应该马上返回*/  
  7.         preempt_disable();  
  8.         /*实现软中断中一个关键数据结构是每个 
  9.         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. }  

下面是软中断的执行

[cpp] view plaincopyprint?
  1. <pre class="cpp" name="code">/*如果在这样的一个检查点(local_softirq_pending()不为0) 
  2. 检测到挂起的软中断,内核调用下面函数处理*/  
  3. asmlinkage void do_softirq(void)  
  4. {  
  5.     __u32 pending;  
  6.     unsigned long flags;  
  7.   
  8.     if (in_interrupt())  
  9.         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. }</pre>  
  20. <pre></pre>  
  21. <p></p>  
  22. <p>具体由__do_softirq函数实现</p>  
  23. <pre class="cpp" name="code">/*读取本地CPU的软中断掩码并执行与每个设置位 
  24. 相关的可延迟函数,__do_softirq只做固定次数的循环 
  25. 然后就返回。如果还有其余挂起的软中断,那么 
  26. 内核线程ksofirqd将会在预期的时间内处理他们*/  
  27. asmlinkage void __do_softirq(void)  
  28. {  
  29.     struct softirq_action *h;  
  30.     __u32 pending;  
  31.     /*把循环计数器的值初始化为10*/  
  32.     int max_restart = MAX_SOFTIRQ_RESTART;  
  33.     int cpu;  
  34.     /*把本地CPU(被local_softirq_pending选中的)软件中断的 
  35.     位掩码复制到局部变量pending中*/  
  36.     pending = local_softirq_pending();  
  37.     account_system_vtime(current);  
  38.     /*增加软中断计数器的值*/  
  39.     __local_bh_disable((unsigned long)__builtin_return_address(0));  
  40.     lockdep_softirq_enter();  
  41.   
  42.     cpu = smp_processor_id();  
  43. restart:  
  44.     /* Reset the pending bitmask before enabling irqs */  
  45.     set_softirq_pending(0);/*清除本地CPU的软中断位图, 
  46.     以便可以激活新的软中断*/  
  47.   
  48.     /*激活本地中断*/  
  49.     local_irq_enable();  
  50.   
  51.     h = softirq_vec;  
  52.   
  53.     do {/*根据pending每一位的的设置,执行对应的软中断 
  54.         处理函数*/  
  55.         if (pending & 1) {  
  56.             int prev_count = preempt_count();  
  57.             kstat_incr_softirqs_this_cpu(h - softirq_vec);  
  58.   
  59.             trace_softirq_entry(h, softirq_vec);  
  60.             h->action(h);/*执行注册的具体的软中断函数*/  
  61.             trace_softirq_exit(h, softirq_vec);  
  62.             if (unlikely(prev_count != preempt_count())) {  
  63.                 printk(KERN_ERR "huh, entered softirq %td %s %p"  
  64.                        "with preempt_count %08x,"  
  65.                        " exited with %08x?\n", h - softirq_vec,  
  66.                        softirq_to_name[h - softirq_vec],  
  67.                        h->action, prev_count, preempt_count());  
  68.                 preempt_count() = prev_count;  
  69.             }  
  70.   
  71.             rcu_bh_qs(cpu);  
  72.         }  
  73.         h++;  
  74.         pending >>= 1;  
  75.     } while (pending);  
  76.   
  77.     local_irq_disable();  
  78.   
  79.     pending = local_softirq_pending();  
  80.     if (pending && --max_restart)  
  81.         goto restart;  
  82.   
  83.     if (pending)/*如果还有挂起的软中断,唤醒内核线程 
  84.             来处理本地CPU的软中断*/  
  85.         wakeup_softirqd();  
  86.   
  87.     lockdep_softirq_exit();  
  88.   
  89.     account_system_vtime(current);  
  90.     _local_bh_enable();/*软中断计数器-1,因而重新激活可延迟函数*/  
  91. }</pre>  
  92. <p>到此,linux内核软中断的大致执行和实现基本上分析完了,中间有很多地方没有注释的,主要是考虑到需要别的实现机制以及有的比较易懂。能够自己看懂。</p> 
0 0
原创粉丝点击