Chinaunix首页 | 论坛 | 博客
  • 博客访问: 715966
  • 博文数量: 183
  • 博客积分: 2650
  • 博客等级: 少校
  • 技术积分: 1428
  • 用 户 组: 普通用户
  • 注册时间: 2008-11-22 17:02
文章分类
文章存档

2017年(1)

2015年(46)

2014年(4)

2013年(8)

2012年(2)

2011年(27)

2010年(35)

2009年(60)

分类: LINUX

2013-08-04 21:17:17

原文地址:软中断(softirq) 作者:leonwang202

一、软中断注册
和硬中断类似,软中断也有类似的中断向量表,只不过是用“软件”实现的。
struct softirq_action softirq_vec[32]是软中断向量表 
(文件linux_2_6_24/kernel/softirq.c)
  1. struct softirq_action
  2. {
  3.     void (*action)(struct softirq_action *); //钩子函数
  4.     void *data;                              //钩子函数的形参
  5. };

内核用到的中断向量(其实就是数组下标)如下所示
(文件linux_2_6_24/include/linux/interrupt.h)
  1. enum{
  2.     HI_SOFTIRQ=0,       //高优先级的tasklet
  3.     TIMER_SOFTIRQ,      //内核定时器
  4.     NET_TX_SOFTIRQ,     //网络发送
  5.     NET_RX_SOFTIRQ,     //网络接收
  6.     BLOCK_SOFTIRQ,      //???
  7.     TASKLET_SOFTIRQ,    //普通的tasklet
  8.     SCHED_SOFTIRQ
  9. }

内核注册一个软中断用函数,本质上就是就是初始化数组某一元素
  1. void open_softirq(int nr, void (*action)(struct softirq_action*), void *data)
  2. {   //nr 即软中断向量编号
  3.     softirq_vec[nr].data = data;
  4.     softirq_vec[nr].action = action;
  5. }

内核注册软中断的地方有:
  1. start_kernel()
  2.     -->init_timers()
  3.         -->open_softirq(TIMER_SOFTIRQ,run_timer_softirq,NULL)
  4.     -->softirq_init()
  5.         -->open_softirq(TASKLET_SOFTIRQ, tasklet_action,NULL)
  6.         -->open_softirq(HI_SOFTIRQ,tasklet_hi_action,NULL)

  7.     -->do_initcall()
  8.         -->net_dev_init()
  9.            -->open_softirq(NET_TX_SOFTIRQ, net_tx_action, NULL);
  10.            -->open_softirq(NET_RX_SOFTIRQ, net_rx_action, NULL);
  11.         -->blk_dev_init()
  12.            -->open_softirq(BLOCK_SOFTIRQ, blk_done_softirq,NULL)

二、软中断触发

前面注册完了,现在开始触发。
内核用一个数据结构来标记曾经有“软中断”发生过(或者说成软中断被触发过)
 __softirq_pending 共32bit,即每个bit对应软中断的一个向量,实际使用了6个bit
第n个bit置1,即softirq_vec[n]有软中断发生。

  1. typedef struct {
  2.     unsigned int __softirq_pending; /* set_bit is used on this */
  3.     unsigned int __last_jiffy_stamp;
  4. } ____cacheline_aligned irq_cpustat_t;

  5. extern irq_cpustat_t irq_stat[]; /* defined in asm/hardirq.h */
  6. #define __IRQ_STAT(cpu, member) (irq_stat[cpu].member)

  7. #define local_softirq_pending() \
  8.     __IRQ_STAT(smp_processor_id(), __softirq_pending)

  9. #define set_softirq_pending(x) (local_softirq_pending() = (x))
  10. #define or_softirq_pending(x) (local_softirq_pending() |= (x))

  11. #define __raise_softirq_irqoff(nr) do { or_softirq_pending(1UL << (nr)); } while (0)

常用的软中断触发函数
  1. void raise_softirq_irqoff(int nr){    //nr 即软中断向量编号
  2.     __raise_softirq_irqoff(nr);
  3. }
但这只是“触发”软中断,软中断并不会立即被处理

三、软中断处理
函数_ _do_softirq是一次性按照向量表从高到低循环处理所有软中断(潜台词,软中断不可嵌套)

_ _do_softirq()的调用时机:

1. irq_exit() 硬件中断处理完,返回时调用
  1. do_IRQ() -->irq_exit()
  2.          -->local_softirq_pending()
  3.          -->_ _do_softirq()

2. ksoftirqd() 内核进程
  1. start_kernel() --> kernel_init() -->do_pre_smp_initcalls()
  2.     -->spawn_ksoftirqd() -->cpu_callback()
  3.     -->kthread_create(ksoftirqd, hcpu, "ksoftirqd/%d", hotcpu)
  4.     -->ksoftirqd()
  5.     -->local_softirq_pending()
  6.     -->_ _do_softirq()

3. local_bh_enable()时,发现有待处理的软中断且当时没处在软硬中断上下文中
  1. local_bh_enable()
  2.     -->local_softirq_pending()
  3.     -->_ _do_softirq()


处理过程代码详解
  1. asmlinkage void __do_softirq(void)
  2. {
  3.     struct softirq_action *h;
  4.     __u32 pending;
  5.     int max_restart = MAX_SOFTIRQ_RESTART;
  6.     int cpu;

  7.     pending = local_softirq_pending();
  8.     account_system_vtime(current);

  9. /*软中断处理中,禁止软中断再次进入,软中断处理是不可重入的*/
  10.     __local_bh_disable((unsigned long)__builtin_return_address(0));
  11.     trace_softirq_enter();
  12.     cpu = smp_processor_id();
  13. restart:
  14.     /* Reset the pending bitmask before enabling irqs
  15.     下面首先清除pending,以便系统可以激活其它软件中断,
  16.     然后使能外部中断
  17.     系统在下面的处理中,将使能外部中断以提高系统的响应,
  18.     注意,必须先清除pending,再使能外部中断,否则死锁*/
  19.     set_softirq_pending(0);

  20.     local_irq_enable();

  21.     h = softirq_vec;

  22. /*下面按照从高到低调用open_softirq注册的句柄*/
  23.     do {
  24.         if (pending & 1) {
  25.             h->action(h);  //关键的一句,tasklet、内核timer、网络中断都是在这里搞的
  26.             rcu_bh_qsctr_inc(cpu);
  27.         }
  28.         h++;
  29.         pending >>= 1;
  30.     } while (pending);

  31.     local_irq_disable();

  32.     pending = local_softirq_pending();
  33.     if (pending && --max_restart)
  34.         goto restart;

  35.     if (pending)
  36.         wakeup_softirqd();

  37.     trace_softirq_exit();

  38.     account_system_vtime(current);
  39.     _local_bh_enable();
  40. }





阅读(4329) | 评论(0) | 转发(0) |
1

上一篇:内核启动参数详解

下一篇:小任务(tasklet)

给主人留下些什么吧!~~