Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1590776
  • 博文数量: 77
  • 博客积分: 1205
  • 博客等级: 少尉
  • 技术积分: 4476
  • 用 户 组: 普通用户
  • 注册时间: 2010-04-22 21:48
文章分类
文章存档

2018年(1)

2017年(1)

2015年(1)

2014年(18)

2013年(12)

2012年(44)

分类: LINUX

2012-05-03 16:43:32

大概有一些同学对tasklet的串行化还有点困惑,其实在单处理器上最好理解,所以本帖主要讨论多处理器上tasklet如何实现串行化:同一个tasklet对象同一时刻只能在一个处理器上运行。

在驱动程序中,tasklet是作为一种softirq形式出现的,所以对tasklet对象的提交一般发生在中断处理例程ISR中。一般一个tasklet用来对同一种中断类型进行后续的处理,所以完全不必要通过动态生成tasklet对象的方式在每次中断到来时重新生成一个tasklet对象来做后半段的处理。事实上Linux内核源码中,几乎所有的tasklet对象都是针对同一类型的中断只产生一个。如果有同学发现有例外的情况,请告诉我,将非常感谢。

假设某个中断发生,由CPU0来处理,在它的ISR中会调用tasklet_schedule来提交一个tasklet对象,假设为tasklet_obj,那么tasklet_schedule首先会为该tasklet对象tasklet_obj.state打上一个标志:TASKLET_STATE_SCHED,表明该tasklet对象被提交但还没有被运行,前述的打标志的操作是个原子,代码里是test_and_set_bit,这意味着如果同时有处理器CPU0和CPU1都来提交该tasklet_obj,那么只有一个会被成功提交,不过不用担心一个tasklet对象没被提交成功的话会有啥副作用,因为在softirq的处理阶段,一个tasklet对象上的处理函数可以一并处理掉外设若干次同一中断要做的事,最典型的,比如网卡连续接收到两个数据包,产生两个中断到两个不同处理器上,因为只有一个tasklet对象被提交,当该对象上的延迟函数被执行时,它会将两个数据包都读到系统内存中。

TASKLET_STATE_SCHED标志是确保tasklet串行化的第一道防线,但是如果该tasklet_obj对象已经被调度到处理器CPU0上运行了,那么TASKLET_STATE_SCHED标志会被清除,这意味着当一个tasklet_obj对象正在一个处理器上运行时,同一个tasklet_obj对象完全可以被提交到另一个处理器,比如CPU1上。那么这种情况下如何确保tasklet串行化呢,答案是tasklet_obj.state上为SMP系统增加的另一个标志位TASKLET_STATE_RUN,当一个tasklet_obj对象被某一处理器开始调度运行时,tasklet_action,也就是对应的softirq处理例程会通过tasklet_trylock来将该tasklet_obj对象的state成员打上标志TASKLET_STATE_RUN,这个操作同样是原子的,因此只会有一个处理器成功完成测试及打标志的动作,没成功的那个处理器上的tasklet_action会把当前的tasklet_obj重新加入到其所管理的tasklet_vec链表的尾部(因为一个相同的tasklet_obj对象已经在运行了,所以再期望其一并完成当前tasklet_obj所表示的任务变得不再可靠,这不同于刚提交时的情形,所以内核对此的策略是,把当前希望运行但是发现已经有同一个tasklet_obj对象的延迟函数正在被执行时,将当前对象加入到处理器tasklet_vec的链表尾部)。那先前成功的处理器就可以开始执行tasklet_obj对象上的延迟函数,执行完毕该tasklet_obj对象将从它所在的处理器tasklet_vec链表中消失,除非再次提交。所以一个成功执行的tasklet_obj对象的状态变化是:
TASKLET_STATE_SCHED(被成功提交)-->TASKLET_STATE_RUN | TASKLET_STATE_SCHED(被提交并且即将被调度运行)-->TASKLET_STATE_RUN(正在被调度执行中)。

而一个不成功的tasklet要么在tasklet_schedule处就被泯灭掉,此时它根本不会出现在任何一个处理器的tasklet_vec链表中,要么是在同一个tasklet对象正在其他处理器上被执行时被成功提交,但是它目前暂时无法被执行,会被放到它所属的处理器tasklet_vec链表的尾部等待下一次被调度运行。
阅读(6735) | 评论(2) | 转发(3) |
给主人留下些什么吧!~~

偶尔胤D2013-04-17 10:43:50

代码如下:
 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);

偶尔胤D2013-04-17 10:43:37

近日看tasklet,对tasklet的可重入晕晕乎乎滴,说下我自己的几点理解及疑问:
1、“事实上Linux内核源码中,几乎所有的tasklet对象都是针对同一类型的中断只产生一个”这句话的意思是说,比如对键盘中断,该中断自始至终操作的都是同一个tasklet对象,该tasklet的func和data字段一般是不会变的,内存中对键盘中断是不是只有这一个tasklet对象??
2、当某中断(如键盘中断)的tasklet对象,就说tasklet_obj吧,在cpu0上运行时,会清除掉SCHED标志,此时tasklet_obj的标志是RUN,所以此时其他cpu可以对tasklet_obj调用tasklet_schedule,但只有一个cpu会执行tasklet_schedule成功,并把tasklet_obj加入到该cpu(比如cpu1)的tasklet_vec上,此时在cpu0上,会调用tasklet_obj的func函数,即tasklet_obj->func(tasklet_obj->data),但其实此刻tasklet_obj已经在cpu1的taskl