Chinaunix首页 | 论坛 | 博客
  • 博客访问: 150040
  • 博文数量: 31
  • 博客积分: 1911
  • 博客等级: 上尉
  • 技术积分: 327
  • 用 户 组: 普通用户
  • 注册时间: 2010-11-29 09:48
文章分类

全部博文(31)

文章存档

2011年(22)

2010年(9)

我的朋友

分类: LINUX

2011-01-15 18:05:58

 软中断的实现 

     今天继续看第7章《下半部和推后执行的工作》。前天本来已经写了《软中断的实现》,但是没保存,所以今天又重新写一次。

      我们首先从头说起。我们一般把中断处理流程切为两个部分或两半。中断处理程序是上半部(top half),对时间要求相对宽松的工作就是下半部(bottom half)的执行目标。

     目前,有三种机制可以用来实现将工作推后执行:软中断、tasklet和工作队列。软中断是一种静态定义的下半部接口,有32个,可以在所有处理器上同时执行---即使两个类型相同也可以。tasklet是一种基于软中断实现的灵活性强、动态创建的下半部实现机制。两个不同类型的tasklet可以在不同的处理器上同时执行,但类型相同的tasklet不能同时执行。软中断是在编译期间静态分配的,tasklet却能够被动态的注册或去除。工作对列和它们完全不同。

     下面说说软中断的实现。

     软中断由softirq_action结构表示,它定义在linux/interrupt.h中:

  1. struct softirq_action
  2. {
  3.     void    (*action)(struct softirq_action *);
  4.     void    *data;
  5. };

      每个被注册的软中断都占据该数组的一项,因此最多可能有32个中断。

软中断的代码位于kernel/softirq.c中。

      这是我第三次写了,昨天没保存只留下上面这些,只好从这里重新开始。再写一次我就快疯了,呜呜!!!

 

1.软中断处理程序

 

软中断处理程序action(softirq_action结构中的action函数指针)的函数原型如下:

void softirq_handler(struct softirq_action *)

当内核运行一个软中断处理程序的时候,它就会执行这个action函数,其唯一的参数就是指向对应的softirq_action结构体的指针。为什么要传这个结构体而不是传数据值呢?这样做的原因是可以保证将来在结构体中加入新的域时,无须对所有的软中断处理程序进行变动。如果需要,软中断处理程序可以方便地解析它的参数,从数据成员中提取数值。In my opinion, we can use  Macro containof.

  1. struct softirq_action *h;
  2. h = softirq_vec[n];
  3. h->action(h);

一个软中断不会抢占另外一个软中断。实际上,唯一可以抢占软中断的是中断处理程序。不过,其他软中断---甚至是相同类型的软中断---可以在其他处理器上同时执行。

 

2.执行软中断

 

一个注册的软中断必须在被标记后才会执行,这就是触发软中断(raising the softirq)。中断处理程序绘制返回前标记它的软中断,使其稍后被执行。(注册软中断和触发软中断在中http://blog.csdn.net/qinzhonghello/archive/2008/11/29/3408420.aspx讲述)

 

在下面情况中,待处理的软中断会被检查和执行:

1)从一个硬件中断代码处返回时

2)在ksoftirqd内核线程中

3)在那些显式检查和执行待处理的软中断的代码中,如网络子系统

 

软中断要在do_softirq()函数中执行:

  1. asmlinkage void do_softirq(void)
  2. {
  3.     __u32 pending;
  4.     unsigned long flags;
  5.     if (in_interrupt())
  6.         return;
  7.     local_irq_save(flags);
  8.     pending = local_softirq_pending();
  9.     if (pending)
  10.         __do_softirq();
  11.     local_irq_restore(flags);
  12. }

具体分析如下:

1)宏in_interrupt()用来判断内核是否处于中断上下文中,如果是它返回非零,说明内核此刻正在执行中断处理程序,或者正在执行下半部处理程序。定义在中:

  1. #define in_interrupt()      (irq_count()) 
  2. #define irq_count() (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK)) 
  3. #define preempt_count() (current_thread_info()->preempt_count) 
  4. /* Return a thread_info struct. */
  5. static inline struct thread_info *current_thread_info(void)
  6. {
  7.     struct thread_info *ti;
  8.     __asm__ __volatile__ ("and.d $sp, %0" : "=r" (ti) : "0" (~8191UL));
  9.     return ti;
  10. }

2)局部变量pending保存宏softirq_pending()的返回值。它是待处理的软中断的32位位图,如果第n为被设置为1,那么第n位对应类型的软中断等待处理。

  1. #define local_softirq_pending() \
  2.     (local_cpu_data().__softirq_pending)

3)函数__do_softirq()

  1. /*
  2.  * We restart softirq processing MAX_SOFTIRQ_RESTART times,
  3.  * and we fall back to softirqd after that.
  4.  *
  5.  * This number has been established via experimentation.
  6.  * The two things to balance is latency against fairness -
  7.  * we want to handle softirqs as soon as possible, but they
  8.  * should not be able to lock up the box.
  9.  */
  10. #define MAX_SOFTIRQ_RESTART 10
  11. asmlinkage void __do_softirq(void)
  12. {
  13.     struct softirq_action *h;
  14.     __u32 pending;
  15.     int max_restart = MAX_SOFTIRQ_RESTART;
  16.     int cpu;
  17.     pending = local_softirq_pending();
  18.     account_system_vtime(current);
  19.     __local_bh_disable((unsigned long)__builtin_return_address(0));
  20.     trace_softirq_enter();
  21.     cpu = smp_processor_id();
  22. restart:
  23.     /* Reset the pending bitmask before enabling irqs */
  24.     set_softirq_pending(0);
  25.     local_irq_enable();
  26.     h = softirq_vec;
  27.     do {
  28.         if (pending & 1) {
  29.             h->action(h);
  30.             rcu_bh_qsctr_inc(cpu);
  31.         }
  32.         h++;
  33.         pending >>= 1;
  34.     } while (pending);
  35.     local_irq_disable();
  36.     pending = local_softirq_pending();
  37.     if (pending && --max_restart)
  38.         goto restart;
  39.     if (pending)
  40.         wakeup_softirqd();
  41.     trace_softirq_exit();
  42.     account_system_vtime(current);
  43.     _local_bh_enable();

下面这段代码是软中断处理的核心部分:

  1.     h = softirq_vec;
  2.     do {
  3.         if (pending & 1) {
  4.             h->action(h);
  5.             rcu_bh_qsctr_inc(cpu);
  6.         }
  7.         h++;
  8.         pending >>= 1;
  9.     } while (pending);

   将指针h指向softirq_vec的第一项,如果pending的第一位被置为1,h->action(h)被调用,指针加1,指向数组中的下一项。位掩码pending右移一位,这样会丢弃第一位,然后让其他各位依次向右移动一个位置。一直重复直到pengding变为0,结束。

 

 

4)函数local_irq_save(flags)和local_irq_restore(flags)

 

用于禁止/激活当前处理器上的本地中断,可以以下语句:

local_irq_disable();

local_irq_enable();

但是,考虑这种情况:如果在调用local_irq_disable()之前已经禁止了中断;如果中断可能在开始时就是关闭的,调用local_irq_enable()将无条件的激活中断。因此,需要有一种机制将中断恢复到以前的状态。函数local_irq_save(flags)在禁止中断之前保存中断系统的状态,local_irq_restore(flags)在准备激活中断时,把中断恢复到它们原来的状态。

 

  1. #define local_irq_restore(flags) do { typecheck(unsigned long, flags); \
  2.                       set_signals(flags); } while(0)
  3. #define local_irq_save(flags) do { local_save_flags(flags); \
  4.                                    local_irq_disable(); } while(0)

这些方法以宏形式出现,因此表面上flags参数(该参数必须被定义为unsiged long 类型)是以值传递的。该参数包含具体体系结构的数据,也就是包含中断系统的状态。至少有一种体系结构把栈信息与值相结合(SPARC),因此flags不能传递给另一个函数(特别是它必须驻留在同一栈帧中)。基于这个原因,对local_irq_save和local_irq_restore()的调用必须在同一个函数中进行。

阅读(2038) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~