feiyang10086 发表于 2011-12-27 22:17

linux软中断


linux软中断







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

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

软中断

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



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



view plaincopy to clipboard01.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 clipboard01.struct softirq_action
02.{
03.    void    (*action)(struct softirq_action *);
04.}; 软中断的初始化



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

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



view plaincopy to clipboard01./*激活软中断*/
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 clipboard01./*
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 clipboard01.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 clipboard01.<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 clipboard01./*读取本地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,
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内核软中断的大致执行和实现基本上分析完了,中间有很多地方没有注释的,主要是考虑到需要别的实现机制以及有的比较易懂。能够自己看懂。

芯忻相依 发表于 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

额。围观。。。。
页: [1]
查看完整版本: linux软中断