·ChinaUnix首页 ·论坛 ·博客 
Linux首页 | Linux新闻 | Linux论坛 | Linux文档 | Linux下载 | Linux博客 | Linux搜索 | 开源项目孵化平台 | 《开源时代》
新手入门 | 安装启动 | 管理员指南 | 开发手册 | 桌面应用 | 程序开发 | 数据库 | 网络技术| CentOS | Fedora | MySQL | Apache | Ubuntu | Gentoo| OSCON08
  Linux时代 >> 技术文档 >> 网络技术
 
中断下半部分析(tasklet)
来源: ChinaUnix博客  日期: 2009.06.08 23:19 (共有条评论) 我要评论
 
------------------------------------------
转载请注明出处:http://lullaby2005.cublog.cn/
------------------------------------------
一、为什么要进入tasklet
我们在softirq的文章中分析过,在SMP系统中,任何一个处理器在响应外设中断请求,完成中断上半部处理后,都可以调用函数do_softirq()来处理构建在softirq机制上的下半部。也就是说,softirq处理函数在SMP系统中是可以并行执行的,这要求使用softirq机制的下半部必须是多处理器可重入的。这对于一般的驱动程序开发者而言, 事情会变得复杂化、难度增大。为了降低驱动开发难度必须提供一套有效的机制,tasklet就是为了解决这一问题而出现的。

二、tasklet实现分析
1.       一个实例
#include
#include
#include
#include
#include
#include
#include

static struct tasklet_struct my_tasklet;  /*定义自己的tasklet_struct变量*/

static void tasklet_handler (unsigned long data)
{
        printk(KERN_ALERT “tasklet_handler is running.\n”);
}

static int __init test_init(void)
{
        tasklet_init(&my_tasklet, tasklet_handler, 0); /*挂入钩子函数tasklet_handler*/
        tasklet_schedule(&my_tasklet); /* 触发softirq的TASKLET_SOFTIRQ,在下一次运行softirq时运行这个tasklet*/  
        return 0;
}

static void __exit test_exit(void)
{
        tasklet_kill(&my_tasklet); /*禁止该tasklet的运行*/
        printk(KERN_ALERT “test_exit running.\n”);
}
MODULE_LICENSE(“GPL”);

module_init(test_init);
module_exit(test_exit);

运行结果如图:



2.       实现分析
我们就从上面这个实例入手来分析tasklet的实现,
在init中,通过函数tasklet_init()来初始化自己需要注册到系统中的tasklet结构:
void tasklet_init(struct tasklet_struct *t,
                void (*func)(unsigned long), unsigned long data)
{
       t->next = NULL;
       t->state = 0;
       atomic_set(&t->count, 0);
       t->func = func;
       t->data = data;
}
很简单,只是初始化tasklet_struct的各个字段,挂上钩子函数。

然后,通过函数tasklet_schedule()来触发该tasklet
static inline void tasklet_schedule(struct tasklet_struct *t)
{
        /*如果需要调度的tasklet的state不为TASKLET_STATE_SCHED,则触发之。这样,就保证了多个cpu不可能同时运行同一个tasklet,因为如果一个tasklet被调度过一次,那么它的state字段就会被设置TASKLET_STATE_SCHED标记,然后插入per-cpu变量的链表中。如果这时另外一个cpu也去调度该tasklet,那么就会在下面的if语句中被挡掉,不会运行到__tasklet_schedule(),从而不会插入到另外这个cpu的per-cpu变量的链表中,就不会被运行到。所以这里是保证了tasklet编写的函数不用是可重入的,这样就方便了编程人员。(注意,softirq机制需要编写可重入的函数)*/
       if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
              __tasklet_schedule(t);
}

我们来看__tasklet_schedule()的实现:
void fastcall __tasklet_schedule(struct tasklet_struct *t)
{
       unsigned long flags;

       local_irq_save(flags);
        /*把需要添加进系统的自己编写的struct tasklet_struc加入
        到per-cpu变量tasklet_vec的本地副本的链表的表头中*/
       t->next = __get_cpu_var(tasklet_vec).list;
       __get_cpu_var(tasklet_vec).list = t;
       raise_softirq_irqoff(TASKLET_SOFTIRQ); /*触发softirq的TASKLET_SOFTIRQ*/  
       local_irq_restore(flags);
}
这段代码也非常简单,只是把自己要注册到系统中的tasklet_struct挂入到per-cpu变量tasklet_vec的list中而已,这里是挂到链表首部。因为需要修改per-cpu变量tasklet_vec的list的值,为了防止中断处理程序也去修改这个值,所以要加自旋锁,为了保持数据的一致性。
然后通过raise_softirq_irqoff()设置低优先级的tasklet对应的softirq标记,以便cpu在运行softirq的时候运行到tasklet,因为tasklet是凌驾在softirq机制之上的。

OK,这里就完成了我们自己的my_tasklet的注册和触发对应的softirq,那我们现在就应该分析tasklet的运行了。
我们前面提到,tasklet是凌驾在softirq机制之上的。还记得前面说到了Linux中有六种softirq,优先级最高的是HI_SOFTIRQ,优先级最低的是TASKLET_SOFTIRQ,一般情况下我们是利用TASKLET_SOFTIRQ来实现tasklet的功能。
       open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);中定义了处理tasklet的处理函数tasklet_action.所以我们要分析这个函数的实现:

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

       /*把per-cpu变量tasklet_vec的本地副本上的list设置为NULL,
       由于这里要修改per-cpu变量,为了防止中断处理程序
       或者内核抢占造成该数据的不一致性,所以这里禁止中断再修改数据
       ,然后再开启中断.(注意,关闭本地中断的副作用就是禁止内核抢占,
       因为内核抢占只有两个时间点: 1.中断返回到内核态;2.手动使能内核抢占。
       明显程序员不会在临界区内手动使能内核抢占,所以关闭本地中断的
       副作用就是禁止内核抢占)*/
       local_irq_disable();
       list = __get_cpu_var(tasklet_vec).list;
       __get_cpu_var(tasklet_vec).list = NULL;
       local_irq_enable();

       /*遍历tasklet链表,让链表上挂入的函数全部执行完成*/
       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); /*真正运行user注册的tasklet函数的地方*/
                            tasklet_unlock(t);
                            continue;
                     }
                     tasklet_unlock(t);
              }

              /*这里相当于把tasklet的list指针从链表中后移了(可以自行画图分析),
              所以刚才运行过的tasklet回调函数以后不会再次运行,除非用于再次
              通过tasklet_schedule()注册之*/
              local_irq_disable();
              t->next = __get_cpu_var(tasklet_vec).list;
              __get_cpu_var(tasklet_vec).list = t;
              __raise_softirq_irqoff(TASKLET_SOFTIRQ);  /*再一次触发tasklet对应的softirq,使下次系统运行softirq时能运行到tasklet*/
              local_irq_enable();
       }
}
运行流程是不是很简单呢?呵呵。只要注意到加锁的时机就OK了!


三、总结
Tasklet与一般的softirq的比较重要的一个区别在于: softirq处理函数需要被编写成可重入的,因为多个cpu可能同时执行同一个softirq处理函数,为了防止数据出现不一致性,所以softirq的处理函数必须被编写成可重入。最典型的就是要在softirq处理函数中用spinlock保护一些共享资源。而tasklet机制本身就保证了tasklet处理函数不会同时被多个cpu调度到。因为在tasklet_schedule()中,就保证了多个cpu不可能同时调度到同一个tasklet处理函数,这样tasklet就不用编写成可重入的处理函数,这样就大大减轻了kernel编程人员的负担。


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u3/96958/showart_1959111.html
  发表评论 查看评论(共有条评论) 我要提问
 
 


最新资讯更多>> 
· 谷歌劝说诺基亚采用Android操作..
· Apache 基金会确认退出 JCP 执..
· Chrome 10 新功能探秘:新增GP..
· 金山宣布开源其安全软件
· 女黑客在开源会议上抱受骚扰
· 21款值得关注的Linux游戏
· 马化腾:腾讯半年后彻底转型,..
· [多图] Chrome OS 预发布版本多..
· Lubuntu 11.04 默认应用抢先一览
· Red Hat宣布收购云计算软件提供..
论坛热点更多>> 
· do_execve时候用户栈中参数的..
· swapinfo -atm 问题
· Linux 的优点简述
· VM虚拟机上得Red Hat Linux上..
· 我看成了上海男人喜欢女人毛..
· 校车展览,看了你就知道
· 在遇到他之前,唯一需要做的..
· GRUB的疑问
· 从来没有人真正付足书价——..
· 云存储 vs 网盘
文档更新更多>> 
· orcale queue
· 谁可以推荐几本经典的操作系统的..
· 【北京】某物联网公司招云计算应..
· 【北京】某物联网公司招云计算应..
· 谁能推荐几本关于操作系统的书
· 如何添加网络接口eth1
· 葡萄牙语入门教材的选取与经验分享
· 葡萄牙语就业前景分析
· 葡萄牙语学习经验交流
· Щ
 
关于我们 | 联系方式 | 广告合作 | 诚聘英才 | 网站地图 | 友情链接 | 免费注册

Copyright © 2001-2009 ChinaUnix.net All Rights Reserved

感谢所有关心和支持过ChinaUnix的朋友们

京ICP证:060528号