中断下半部分析之Tasklet

上一个博客写了中断下半部的软中断softirq部分的一些学习笔记,这批博客接着写tasklet微任务部分。

仅仅是平时看代码中的一些笔记,记下了怕忘了。


Tasklet是中断下半部的一种,它工作在中断上下文。同一个tasklet对象同一时刻只能在一个cpu上运行

Tasklet的数据结构为

struct tasklet_struct  

  

   struct tasklet_struct *next 

   unsigned long state 

   atomic_t count;  

   void (*func)(unsigned long);  

   unsigned long data 

 };  

各个字段的含义如下:

字段

含义

state

Tasklet的状态,主要包括两种状态:

TASKLET_STATE_SCHED   表示此tasklet已被调度,单还未执行

TASKLET_STATE_RUN     表示此tasklet已在cpu上运行

count

引用计数值,不为0表示此tasklet被禁止,不允许执行

func

要执行的函数

data

函数的参数

这里不分析tasklet的用法,只是看下tasklet是如何做到串行化的,也就是同一个tasklet对象同一时刻只能在一个cpu上运行。

调用tasklet_schedule来提交一个tasklet对象,该函数首先设置此tasklet对象的state字段为TASKLET_STATE_SCHED状态,表明该tasklet对象已经被提交但还没有被运行。


static inline void tasklet_schedule(struct tasklet_struct *t

  

   if (!test_and_set_bit(TASKLET_STATE_SCHED&t->state))  

                 __tasklet_schedule(t);  

  

tasklet_schedule函数首先调用test_and_set_bit设置state字段为TASKLET_STATE_SCHED,并返回它之前的值,如果之前值为0(即是TASKLET_STATE_SCHED状态),那么就调用__tasklet_schedule函数。这里说明一个tasklet对象在已调度还未执行之前,另一个相同的tasklet对象又被调度了,它也只会执行一次。如果test_and_set_bit返回值为非0,及state字段状态值为TASKLET_STATE_RUN,那么就不会调用__tasklet_schedule函数了,而是直接返回。


void __tasklet_schedule(struct tasklet_struct *t

  

   unsigned long flags 

  

   local_irq_save(flags);  

   t->next = NULL 

   *__this_cpu_read(tasklet_vec.tailt 

   __this_cpu_write(tasklet_vec.tail&(t->next));  

   raise_softirq_irqoff(TASKLET_SOFTIRQ);  

   local_irq_restore(flags);  

 }


这段代码主要就是将此tasklet对象加入所在cpupercp数组tasklet_vec中。raise_softirq_irqoff函数会设置软中断中TASKLET_SOFTIRQ相应位为pending状态。

test_and_set_bit操作是原子操作,如果同时有两个处理器同时调用tasklet_schedule函数来调度同一个tasklet对象,那么只有一个会被成功提交。

 

TASKLET_STATE_SCHED是确保tasklet串行化执行的第一道防线,如果一个tasklet对象已被调度到处理器上运行,那么TASKLET_STATE_SCHED标志就会被清除。

static void tasklet_action(struct softirq_action *a 

 

  struct tasklet_struct *list 

  while (list) {  

   struct tasklet_struct *t list 

 

   list list->next 

   if (tasklet_trylock(t))  /*还未被其他处理器调度执行*/

    if (!atomic_read(&t->count)) { /*处于激活状态,允许执行*/  

     if (!test_and_clear_bit(TASKLET_STATE_SCHED&t->state))  

      BUG();  

     t->func(t->data);  

     tasklet_unlock(t);  

     continue 

     

    tasklet_unlock(t);  

    

          }   

} 

tasklet_trylock函数会调用test_and_set_bit原子操作判断此tasklet对象是否已被其他的cpu调度开始执行了,具体是判断tasklet对象的state字段是否设置为TASKLET_STATE_RUN状态。tasklet_trylock成功表明此tasklet对象还没有被调度执行。

tasklet_trylock成功之后,原子读取count字段,如果此字段为0,表明此tasklet处于激活状态。如果此字段为非0,表明此tasklet被禁止,不允许执行。

接下来调用test_and_clear_bit清除掉TASKLET_STATE_SCHED标志。这样其他CPU就可以调用tasklet_schedule函数提交此tasklet对象。

正是通过TASKLET_STATE_RUN标志保证了SMP架构下面的同一tasklet对象同一时刻的串行化执行。


你可能感兴趣的:(linux,内核,中断)