linux 软中断和tasklet

1. 软中断是什么 ?

 软中断是一种延时机制,代码执行的优先级比进程要高,比硬中断要低。相比于硬件中断,软中段是在开中断的环境中执行的(长时间关中断对系统的开销太大), 代码是执行在中断/线程上下文的,是不能睡眠的,虽然每个cpu都有一个对应的ksoftirqd/n线程来执行软中断,但是do_softirq这个函数也还会在中断退出时调用到,因此不能睡眠(中断上下文不能睡眠的原因是由于调度系统是以进程为基本单位的,调度时会把当前进程的上下文保存在task_struct这个数据结构中,当进程被调度重新执行时会找到执行的断点,但是中断上下文是没有特定task_struct结构体的,当然现在有所谓的线程话中断,可以满足在中断处理函数执行阻塞操作,但是实时性可能会有问题。还有就是中断代表当前进程执行的概念,个人感觉有点扯淡,毕竟整个内核空间是由所有进程共享的,不存在代表的概念)

2. 软中段是怎么实现的?

一个模块或者子系统的实现都是数据结构+算法来实现的,算法一般由特定的函数来表征。数据结构一般会定义一些全局变量来支撑算法的实现。软中断牵涉到的数据结构主要是一个全局的向量数组,来映射一个软中断向量号和对应的处理函数:

[plain]  view plain copy print ?
  1. static struct sotfirq_action softirq_vec[NR_SOFTIRQS];  
  2. struct softirq_action {  
  3. void (*action)(struct softirq_action *);  
  4. }  

目前代码中有10种类型的软中断

[plain]  view plain copy print ?
  1. enum  {  
  2.         HI_SOFTIRQ=0,  
  3.         TIMER_SOFTIRQ,  
  4.         NET_TX_SOFTIRQ,  
  5.         NET_RX_SOFTIRQ,  
  6.         BLOCK_SOFTIRQ,  
  7.         BLOCK_IOPOLL_SOFTIRQ,  
  8.         TASKLET_SOFTIRQ,  
  9.         SCHED_SOFTIRQ,  
  10.         HRTIMER_SOFTIRQ,  
  11.         RCU_SOFTIRQ,  
  12.   
  13.         NR_SOFTIRQS  
  14. }  

这里只关注和tasklet相关的软中断: HI_SOFTIRQ和TASKLET_SOFTIRQ

核心函数:

[plain]  view plain copy print ?
  1. open_softirq(int nr, void (*action)(struct softirq_action *))  //一个软中断和对应的处理函数的绑定,  
  2. {  
  3.         softirq_vec[nr].action = action;  
  4. }  
[plain]  view plain copy print ?
  1. raise_softirq(unsingned int nr)   //表明一个软中断处于pending状态等待处理, 在do_softirq函数里会检查处于pending的软中断,然后调用对应的处理函数  
  2. {  
  3.         or_softirq_pending(1UL << nr);    // 主要置位per-cpu类型的irq_stat.__softirq_pending成员的第nr位  
  4.         if (!in_interrupt())   //如果不在中断上下文,则唤醒内核线程ksoftirqd/n来处理pending的软中断,如果在中断上下文的话,可能是由于软中断被禁止执行了  
  5.             wakeup_softirqd();   
  6. }  
[plain]  view plain copy print ?
  1. do_softirq(void)  
  2. {  
  3.         if(in_interrupt())    // 这里有两种情况:do_softirq已经在中断上下文被调用一次了,例如在执行软中断的时候又发生了硬件中断,在中断退出的时候又会去执行软中断,防止代码的重入,第二种情况是软中断系统被显示地禁止执行了(全局变量preempt_count的第二个字段softirq counter用来记录软中断被disable的次数)  
  4.              return;  
  5.         pending = local_softirq_pending();  
  6.         if (pending)  
  7.              __do_softirq();  
  8.                  -->__local_bh_disable(); // 进入软中断的处理前,会显示地disable掉软中断,防止重入  
  9.                  --> do {  
  10.                            if (pending & 1) {  
  11.                                softirq_vec[nr]->action(h); //调用对应的软中断处理函数  
  12.                            }  
  13.                            pending >>=1;  //优先级从0开始  
  14.                          }  while (pending);  
  15. }  


3. tasklet的概念和实现

驱动程序里最常见的就是在中断处理函数里面调度一个tasklet来执行延时的任务,tasklet是在软中断的基础之上实现的,牵涉到HI_SOFTIRQ和TASKLET_SOFTIRQ两种软中断,两种软中断对应的处理函数tasklet_action/tasklet_hi_action会去tasklet_vec和tasklet_hi_vec数组(基于per cpu结构)中取所存在的tasklet,然后执行相应的tasklet处理函数。

[plain]  view plain copy print ?
  1. static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);   //数组中的元素为一个链表头, 所有的tasklet是链接在一个链表上的  
  2. static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);  
  3.   
  4. struct tasket_head {  
  5.         struct tasklet_struct *head;  
  6.         struct tasklet_struct **tail;  
  7. }  
  8.   
  9. struct tasklet_struct {  
  10.         struct tasklet_struct *next;  
  11.         unsigned long state;    //一个tasklet对象所处的状态,可以为TASKLET_STATE_SCHED或者TASKLET_STATE_RUN  
  12.         atomic_t count;     //不为0表示禁止执行tasklet  
  13.         void (*func)(unsigned long);  
  14.         unsigned data;  
  15. }  
  16.   
  17. static inline void tasklet_schedule(struct tasklet_struct *t)  
  18. {  
  19.          if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))  //同一个tasklet只能被调度一次,如果另一个CPU正在执行同一个tasklet,如果刚好清掉TASKLET_STATE_SCHED标志,这个tasklet可以被挂到这个CPU上的tasklet_vec链表上,但是不能被这个CPU被执行,因为在执行tasklet之前,会检查当前tasklet的状态是否为TASKLET_STATE_RUN, 如果被置位,则不会执行    
  20.                __tasklet_schedule(t);  //将该tasklet链接到本地CPU对应的tasklet链表上去  
  21.                     -->raise_softirq_irqoff(TASKLET_SOFTIRQ); //通知软中断系统,TASKLET_SOFTIRQ类型的软中断处于pending状态  
  22. }  
  23.   
  24. void tasklet_action(struct softirq_action *a)  
  25. {  
  26.           struct tasklet_struct *list = __this_cpu_read(tasklet_vec.head);  
  27.   
  28.           while(list) {  
  29.                 struct tasklet_struct *t = list;  
  30.                 list = list->next;  
  31.                 if (tasklet_trylock(t)) {   //测试TASKLET_STATE_RUN标志有没有置位,然后置位该标志位  
  32.                         if(!atomic_read(&t->count)) {   //该tasklet被disabled了吗?  
  33.                               if(!test_and_clear_bit(TASKLET_STATE_SCHED, &t->sched))  
  34.                                           BUG();  
  35.                         t->func(t->data);  
  36.                         tasklet_unlock(t);  
  37.                         continue;  
  38.                         }  
  39.                         tasklet_unlock(t);  //复位TASKLET_STATE_RUN标志位  
  40.                  }  
  41.            }  
  42.   
  43. }  
  44.   
  45.   
  46. static inline void tasklet_disable(struct tasklet_struct *t)  
  47. {  
  48.           atomic_inc(&t->count);    
  49.           tasklet_unlock_wait(t); //等待正在执行的tasklet执行完毕,确保调用该函数后,在调用tasklet_schedule后tasklet是不会被执行的  
  50. }  
  51.   
  52. void tasklet_kill(struct tasklet_struct *t)  //感觉这个函数和tasklet_disable类似,只是等待当前正在执行的tasklet完成,下次调用tasklet_schedule应该还是会执行的,貌似和kill这个单词不匹配啊,还没tasklet_disable彻底  
  53. {  
  54.           while(test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {  
  55.                 do {  
  56.                    yield();  
  57.                  } while(test_bit(TASKLET_STATE_SCHED, &t->sched));  
  58.           }  
  59.           tasklet_unlock_wait(t);  
  60.           clear_bit(TASKLET_STATE_SCHED, &t->state);  
  61. }  

从上面的分析可以看出,tasklet的处理函数是不可重入的,换句话说,就是不可能在两个CPU上跑相同的代码,因为一个tasklet只能被调度一次,也就是挂接在一个链表里面,而且在哪个CPU上被调度,就会在哪个CPU被执行,因为是基于per cpu结构的, 但是软中断对应的处理函数是可重入的, 需要处理同步的问题,不过一般驱动里面还是用tasklet,简单方便,而且软中断是在编译时决定的,开销太大。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值