Chinaunix首页 | 论坛 | 博客
  • 博客访问: 438708
  • 博文数量: 99
  • 博客积分: 65
  • 博客等级: 民兵
  • 技术积分: 1012
  • 用 户 组: 普通用户
  • 注册时间: 2012-04-20 16:30
个人简介

linux kernel 工程师

文章分类

全部博文(99)

文章存档

2018年(5)

2017年(12)

2016年(27)

2015年(10)

2014年(43)

2012年(2)

我的朋友

分类: LINUX

2014-02-05 16:07:25

1. tasklet的特点
a. tasklet是softirq的一种, tasklet只可以在一个CPU上同步地执行, 不同的tasklet可以在不同地CPU上同步地执行。 
在softirq中,
HI_SOFTIRQ TASKLET_SOFTIRQ是两种tasklet,但实现基本一样。只是HI_SOFTIRQ位于softirq_vec[]的0位置,在__do_softirq函数中,判断local_softirq_pending是从0bit开始的,所以优先级高。 
enum
{
 HI_SOFTIRQ=0,
 TIMER_SOFTIRQ,
 NET_TX_SOFTIRQ,
 NET_RX_SOFTIRQ,
 BLOCK_SOFTIRQ,
 BLOCK_IOPOLL_SOFTIRQ,
 TASKLET_SOFTIRQ,  
SCHED_SOFTIRQ,
 HRTIMER_SOFTIRQ,
 RCU_SOFTIRQ,    /* Preferable RCU should always be the last softirq */

 NR_SOFTIRQS
};
 看一下softirq_init,
TASKLET_SOFTIRQ,   HI_SOFTIRQ两个向量分别安装了tasklet_action和 tasklet_hi_action两个处理函数

void __init softirq_init(void)
{
  int cpu;

for_each_possible_cpu(cpu) {
   int i;

  per_cpu(tasklet_vec, cpu).tail =
    &per_cpu(tasklet_vec, cpu).head;    /*tasklet_vec的 tail指向&head */
   per_cpu(tasklet_hi_vec, cpu).tail =
    &per_cpu(tasklet_hi_vec, cpu).head; /* tasklet_hi_vec的 tail指向&head */
   for (i = 0; i < NR_SOFTIRQS; i++)
    INIT_LIST_HEAD(&per_cpu(softirq_work_list[i], cpu));
  }

register_hotcpu_notifier(&remote_softirq_cpu_notifier);

open_softirq(TASKLET_SOFTIRQ, tasklet_action);
 open_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

 b. tasklet的串行化使tasklet函数不必是可重入的,因此简化了设备驱动程序开发者的工作

2. tasklet的数据结构

enum 

TASKLET_STATE_SCHED,    /* 1已经被调度,0表示还没调度*/ 
TASKLET_STATE_RUN   /* 1tasklet正在执行,0表示尚未执行,只针对SMP有效,单处理器无意义 */ 
}; 

struct tasklet_struct
{
 struct tasklet_struct *next;
 unsigned long state; /* 用来记录TASKLET_STATE_SCHED,TASKLET_STATE_RUN */
 atomic_t count;   /* 用来禁止tasklet */
 void (*func)(unsigned long); /* tasklet的callback函数 */
 
unsigned long data;
};

/*
 * Tasklets
 */
struct tasklet_head
{
 struct tasklet_struct *head;
 struct tasklet_struct **tail; /* 注意tail指向指针的地址 */
};

/* 每个cpu上都有一个tasklet的链表 */
static DEFINE_PER_CPU(struct tasklet_head, tasklet_vec);
static DEFINE_PER_CPU(struct tasklet_head, tasklet_hi_vec);

3. 如何调度一个tasklet

/* 调度一个tasklet,就是设置TASKLET_STATE_SCHED bit, 并把一个tasklet_struct放到tasklet_vec的尾部,并触发一次softirq */
static inline void tasklet_schedule(struct tasklet_struct *t)
{
 if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) /*  这个判断保证tasklet只会处于某一个cpu的tasklet_vec上,而不会在多个vec上 */
  __tasklet_schedule(t);
}
void __tasklet_schedule(struct tasklet_struct *t)
{
 unsigned long flags;

 local_irq_save(flags);
 t->next = NULL;
 *__this_cpu_read(tasklet_vec.tail) = t;     /* *tail=t */
 __this_cpu_write(tasklet_vec.tail, &(t->next));     /* tail=&t->next */
 raise_softirq_irqoff(TASKLET_SOFTIRQ);
 local_irq_restore(flags);
}

4. tasklet如何执行

 static void tasklet_action(struct softirq_action *a)
{
 struct tasklet_struct *list;

/* 在中断关闭的情况下,摘下本cpu 对应 tasklet_vec链表*/
 local_irq_disable();
 list = __this_cpu_read(tasklet_vec.head);
 __this_cpu_write(tasklet_vec.head, NULL);  /* head=NULL */
 __this_cpu_write(tasklet_vec.tail, &__get_cpu_var(tasklet_vec).head);  /* tail=&head */
 local_irq_enable();

/* 遍历刚刚摘下的链表,如果可以执行,则执行,否则重新放回tasklet_vec */
 while (list) {
  struct tasklet_struct *t = list;    /* 摘下一个tasklet_struct */

  list = list->next;

  if (tasklet_trylock(t)) { /* 实际上是判断state的TASKLET_STATE_RUN bit,如果该bit已经为1,表示别的cpu正在run这个tasklet。 */
    if (!atomic_read(&t->count)) {/* 如果没有禁止 */
    if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) /*  clear sched bit,clear 之后就可以被其他cpu schedule了,也就是说可以加入别的cpu的tasklet_vec了,但由于此时TASKLET_STATE_RUN bit还没有clear,所以虽然此tasklet已经能够加入到别的cpu的tasklet_vec, 但还必须等到这里 TASKLET_STATE_RUN bit clear之后才能够run*/
      BUG();
    trace_irq_tasklet_low_entry(t);
    t->func(t->data);   /* tasklet的callback函数在这里执行 */
    trace_irq_tasklet_low_exit(t);
    tasklet_unlock(t);
    continue;
   }
   tasklet_unlock(t);
  }

  local_irq_disable();   /* 把没有执行的t重新放回尾部,等下次softirq来执行 */
  t->next = NULL;
  *__this_cpu_read(tasklet_vec.tail) = t;
  __this_cpu_write(tasklet_vec.tail, &(t->next));
  __raise_softirq_irqoff(TASKLET_SOFTIRQ);  /* 再次触发softirq*/
  local_irq_enable();
 }
}


 

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