tasklet的使用方法在之前也有讲过,但是不够全面,而且也仅仅知道怎么使用而已,现在看看被人的总结:

//初始化tasklet_struct结构体
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data);//使能一个之前被disable的tasklet;若这个tasklet已经被调度, 它会很快运行。
void tasklet_enable(struct tasklet_struct *t); //函数暂时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在运行, 这个函数忙等待直到这个tasklet退出
void tasklet_disable(struct tasklet_struct *t); //和tasklet_disable类似,但是tasklet可能仍然运行在另一个 CPU
void tasklet_disable_nosync(struct tasklet_struct *t); //调度 tasklet 执行,如果tasklet在运行中被调度, 它在完成后会再次运行; 这保证了在其他事件被处理当中发生的事件受到应有的注意. 这个做法也允许一个 tasklet 重新调度它自己
void tasklet_schedule(struct tasklet_struct *t); //和tasklet_schedule类似,只是在更高优先级执行。
void tasklet_hi_schedule(struct tasklet_struct *t); //确保了 tasklet 不会被再次调度来运行,通常当一个设备正被关闭或者模块卸载时被调用。如果 tasklet 正在运行, 这个函数等待直到它执行完毕。
void tasklet_kill(struct tasklet_struct *t);

这里就深入的看看其实现方法,现在先来一个tasklet的使用案例:

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include <linux/delay.h>static void my_tasklet_function(unsigned long data);char my_tasklet_data[]="my_tasklet_function was  called.";//1.1)动态定义结构体变量,用于动态初始化
struct tasklet_struct my_tasklet;//2)定义tasklet工作函数
static void my_tasklet_function(unsigned long data)
{printk("%s\r\n",(char *)data);udelay(2000);    //延时2ms,而不是休眠
}static int __init tasklet_module_init(void)
{printk("mondule insmod\r\n");//1.2)动态初始化tasklet_init(&my_tasklet, my_tasklet_function,(unsigned long) my_tasklet_data);//3)调度tasklettasklet_schedule(&my_tasklet);return 0;
}static void __exit tasklet_cleanup(void)
{printk("mondule remove\r\n");//4)销毁tasklet tasklet_kill(&my_tasklet);
}module_init(tasklet_module_init);
module_exit(tasklet_cleanup);
MODULE_LICENSE("GPL");

所以我们只要弄明白下面的几个函数是怎么实现的就可以了:

  1. struct tasklet_struct结构体
struct tasklet_struct
{struct tasklet_struct *next;//指针,用于建立tasklet_struct实例的链表,允许多个任务在排队//表示任务当前的状态,一共有两种状态://TASKLET_STATE_SCHED:表示等待调度,注册tasklet的时候会初始化为该状态//TASKLET_STATE_RUN:表示tasklet当前正在执行unsigned long state;atomic_t count;//原子锁计数器,用于同步tasklet_struct的修改void (*func)(unsigned long);//最重要的成员:回调函数unsigned long data;//回调函数的执行参数
};

主要成员就这么几个,next指针主要是把所有的tasklet_struct连起来,state表示tasklet_struct的状态是在排队还是在执行,count是原子锁计数器,他的作用是保护tasklet_struct不会被同时修改,func是回调的执行函数,data就是执行函数的参数。

  1. 初始化函数:void tasklet_init(struct tasklet_struct *t, void (*func)(unsigned long), unsigned long data)
void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long data)
{//初始化tasklet_struct结构体成员t->next = NULL;t->state = 0;atomic_set(&t->count, 0);t->func = func;t->data = data;
}
EXPORT_SYMBOL(tasklet_init);

可以看出tasklet_init的作用就是初始化tasklet_struct 结构体。

  1. 调度函数:tasklet_schedule (struct tasklet_struct *t)
static inline void tasklet_schedule(struct tasklet_struct *t)
{//设置tasklet的状态为TASKLET_STATE_SCHED,使用__tasklet_schedule进行调度if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))__tasklet_schedule(t);
}void __tasklet_schedule(struct tasklet_struct *t)
{__tasklet_schedule_common(t, &tasklet_vec,TASKLET_SOFTIRQ);
}
EXPORT_SYMBOL(__tasklet_schedule);static void __tasklet_schedule_common(struct tasklet_struct *t,struct tasklet_head __percpu *headp,unsigned int softirq_nr)
{struct tasklet_head *head;unsigned long flags;local_irq_save(flags);//保存中断状态寄存器并且关闭本地cpu中断head = this_cpu_ptr(headp);//获取per cpu变量的线性地址t->next = NULL;//初始化tasklet_struct的next指针*head->tail = t;//把tasklet_struct放到cpu调度链表中head->tail = &(t->next);//设置head->tail的地址,用于存放下一个tasklet_structraise_softirq_irqoff(softirq_nr);//唤醒第softirq_nr个softirq准备执行local_irq_restore(flags);//恢复中断状态寄存器
}

调度函数的首先设置tasklet_struct的状态,然后最后调用__tasklet_schedule_common关闭本地cpu中断,把tasklet_struct 放到cpu的tasklet_head调度链表中,让cpu自己找到tasklet_struct并且调度其中的回调函数。

  1. 销毁函数:void tasklet_kill(struct tasklet_struct *t)
void tasklet_kill(struct tasklet_struct *t)
{//如果在中断状态,则提醒一下if (in_interrupt())pr_notice("Attempt to kill tasklet from interrupt\n");//等待调度完毕while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) {do {yield();//让出cpu} while (test_bit(TASKLET_STATE_SCHED, &t->state));}tasklet_unlock_wait(t);//等待解锁//清除tasklet_struct的状态clear_bit(TASKLET_STATE_SCHED, &t->state);
}
EXPORT_SYMBOL(tasklet_kill);

销毁函数就是在等待调度完毕后让出cpu,最后清除tasklet_struct 的状态。

看到这里有人就奇怪了,那到底是怎么调用到func函数的呢?
这就要说到linux中的软中断了,因为tasklet是通过软中断实现的,我们下面看看软中断的初始化函数把,函数在kernel/softirq.c文件中:

void __init softirq_init(void)
{int cpu;//初始化每个cpu的tasklet_vec链表for_each_possible_cpu(cpu) {per_cpu(tasklet_vec, cpu).tail =&per_cpu(tasklet_vec, cpu).head;per_cpu(tasklet_hi_vec, cpu).tail =&per_cpu(tasklet_hi_vec, cpu).head;}//打开TASKLET_SOFTIRQ的软中断,中断回调函数为tasklet_actionopen_softirq(TASKLET_SOFTIRQ, tasklet_action);//打开HI_SOFTIRQ的软中断,中断回调函数为tasklet_hi_actionopen_softirq(HI_SOFTIRQ, tasklet_hi_action);
}

软中断无非就是初始化每一个cpu的tasklet_vec链表,然后打开TASKLET_SOFTIRQ和HI_SOFTIRQ的软中断,我们先看看open_softirq是怎么打开软中断的:

void open_softirq(int nr, void (*action)(struct softirq_action *))
{softirq_vec[nr].action = action;
}

其实只是把中断向量数组的action成员赋值上该中断的回调函数而已,那么每次该中断产生,就会去到回调函数执行了,是不是很简单呢。
我们再看看回调函数tasklet_action是怎么找到我们的tasklet_struct的:

static __latent_entropy void tasklet_action(struct softirq_action *a)
{tasklet_action_common(a, this_cpu_ptr(&tasklet_vec), TASKLET_SOFTIRQ);
}static void tasklet_action_common(struct softirq_action *a,struct tasklet_head *tl_head,unsigned int softirq_nr)
{struct tasklet_struct *list;local_irq_disable();//关闭本地中断,禁止内核抢占list = tl_head->head;//记录tasklet_head指针tl_head->head = NULL;//赋值head,让链表为空tl_head->tail = &tl_head->head;//赋值tail,让链表为空local_irq_enable();//开启中断//遍历tasklet链表,让链表上挂入的函数全部执行完成while (list) {//从链表上摘下每一个tasklet_struct结构体struct tasklet_struct *t = list;list = list->next;//根据tasklet_struct的state成员,判断是否RUNINGif (tasklet_trylock(t)) {if (!atomic_read(&t->count)) {//如果原子锁计数器为0,则可以执行//执行前需要先改变state为RUNINGif (!test_and_clear_bit(TASKLET_STATE_SCHED,&t->state))BUG();t->func(t->data);//真正运行user注册的tasklet函数的地方tasklet_unlock(t);//运行完毕,清除RUNING状态continue;}tasklet_unlock(t);//如果原子锁计数器不为0,清除RUNING状态}local_irq_disable();//关闭本地cpu中断//把以上没有处理完的tasklet重新挂到tasklt_vec数组中对应的cpu上的链表t->next = NULL;*tl_head->tail = t;tl_head->tail = &t->next;__raise_softirq_irqoff(softirq_nr);//把本地CPU的TASKLET_SOFTIRQ标记挂起local_irq_enable();//使能中断}
}

在中断回调函数tasklet_action主要是调用tasklet_action_common来完成tasklet_struct的fun实现,首先还是关闭本地中断,禁止内核抢占,再取出tasklet_head链表并且清空tasklet_head链表,最后把中断开回来,然后再遍历tasklet链表,在while循环中执行fun函数,并且把没有处理的tasklet重新挂到tasklt_vec中。

如果还想知道软中断是怎么通知cpu执行的,请看下集。

tasklet内核源代码分析相关推荐

  1. 《LINUX3.0内核源代码分析》第一章:内存寻址

    https://blog.csdn.net/ekenlinbing/article/details/7613334 摘要:本章主要介绍了LINUX3.0内存寻址方面的内容,重点对follow_page ...

  2. 《LINUX3.0内核源代码分析》第二章:中断和异常 【转】

    转自:http://blog.chinaunix.net/uid-25845340-id-2982887.html 摘要:第二章主要讲述linux如何处理ARM cortex A9多核处理器的中断.异 ...

  3. linux内核源代码分析----内核基础设施之klist

    概述 klist是list的线程安全版本,他提供了整个链表的自旋锁,查找链表节点,对链表节点的插入和删除操作都要获得这个自旋锁.klist的节点数据结构是klist_node,klist_node引入 ...

  4. Linux内核源代码分析-目录

    第一部分 Linux 内核源代码 arch/i386/kernel/entry.S 2 arch/i386/kernel/init_task.c 8 arch/i386/kernel/irq.c 8 ...

  5. Linux内核源代码分析——可执行文件header处理(二进制文件读写范例,写DUL工具入门指引)...

    在把Linux内核源代码生成Image之前,需要把执行文件头结构信息剔除出来.这个过程对理解Linux内核具有很大的帮助.同时,由于是对可执行文件进行直接读写操作,想写DUL工具的童鞋可以在这里学习到 ...

  6. linux VFS概述以及内核源代码分析

    linux VFS概述以及内核源代码分析 一.   概述 Linux能够支持各种不同的文件系统是通过VFS实现的,由于不同的物理文件系统具有不同的组织结构和不同的处理方式,为了能够处理各种不同的物理文 ...

  7. Linux内核源代码分析——fork()原理多进程网络模型

    今晚和一位500强的leader喝喝小酒吃吃烤鱼,生活乐无边.这位兄弟伙才毕业2年,已经做到管理层了,机遇和能力不可谓不好.喝酒之余,聊到Linux内核的两个问题--fork().exec()的原理. ...

  8. LSM内核源代码分析与测试(二)

    LSM内核相关源代码分析见:http://blog.csdn.net/lwyeluo/article/details/55215686 本文修改内核代码来测试自定义的安全模块 测试 操作系统ubunt ...

  9. Linux内核源代码分析经验

      Linux的最大的好处之一就是它的源码公开.同时,公开的核心源码也吸引着无数的电脑爱好者和程序员:他们把解读和分析Linux的核心源码作为自己的 最大兴趣,把修改Linux源码和改造Linux系统 ...

最新文章

  1. MOSS 权限管理总结
  2. host文件修改后无法保存的问题
  3. python工程师薪资坑吗-6年Python开发工程师精心总结学习思路,再不看看就凉了...
  4. 安装VMWare时 the system administrator has set policies to prevent this installation
  5. PMAboutView
  6. 补第一阶段冲刺站立会议1(应发表日期5月13日)
  7. Object对象具体解释(二)之clone
  8. android自动让输入框上划,Android界面技巧:当输入法调出时,如何让界面自动上移,使输入法不会遮挡到主界面(Activity)...
  9. ruby hash方法_Ruby中带有示例的Hash.select方法
  10. redis笔记_源码_双端链表list
  11. C语言 ASCII码字符表
  12. 常见前端bug及其修复方法_50个最常见的WordPress错误及其修复方法
  13. 仿今日头条小程序搭建
  14. java定时自动关机软件
  15. LCA 最近公共祖先 (倍增算法)
  16. 浏览器是先执行js还是先加载HTML,在HTML中使用JavaScript(浏览器对js的加载机制分析)...
  17. 积分第一中值定理与伏汝兰尼(Froullani)积分
  18. 跑步耳机哪种好,目前最适合运动的五款耳机推荐
  19. 刷题 - 算法(一)
  20. 可视化股票逐笔数据分析工具分享

热门文章

  1. C#學習基礎------事件和索引指示器
  2. Python3 GUI:PyQt5环境搭建
  3. 20145302张薇 《信息安全系统设计基础》第14周学习总结
  4. 【转】JAVA 读写二进制文件
  5. GDI+ 中发生一般性错误(在 OutputStream 中保存 PNG 格式图像时遇到的问题)
  6. poj3461kmp
  7. 性能测试利器工具来了,生产环境全链路压测工具
  8. 计算机句法分析的研究现状,计算机理论论文融合语义和句型信息的中文句法分析方法研究与实现...
  9. python时间库_Python处理日期时间的标准库:time和datetime
  10. spgwr | R语言与地理加权回归(Ⅰ-2):广义线性地理加权回归