linux下面的中断处理软件中断tasklet机制
參考:
《Linux内核设计与实现》
http://blog.csdn.net/fontlose/article/details/8279113
http://blog.chinaunix.net/uid-27212029-id-3386692.html
tasklet是中断处理下半部分最经常使用的一种方法,驱动程序一般先申请中断,在中断处理函数内完毕中断上半部分的工作后调用tasklet。tasklet有例如以下特点:
1.tasklet仅仅能够在一个CPU上同步地运行,不同的tasklet能够在不同地CPU上同步地运行。
2.tasklet的实现是建立在两个软件中断的基础之上的,即HI_SOFTIRQ和TASKLET_SOFTIRQ,本质上没有什么差别,仅仅只是HI_SOFTIRQ的优先级更高一些
3.因为tasklet是在软中断上实现的,所以像软中断一样不能睡眠、不能堵塞,处理函数内不能含有导致睡眠的动作,如降低信号量、从用户空间拷贝数据或手工分配内存等。
4.一个 tasklet 可以被禁止而且之后被又一次使能; 它不会运行直到它被使能的次数与被禁止的次数同样.
5.tasklet的串行化使tasklet函数不必是可重入的,因此简化了设备驱动程序开发人员的工作。
6.每一个cpu拥有一个tasklet_vec链表,详细是哪个cpu的tasklet_vec链表,是依据当前线程是执行在哪个cpu来决定的。
tasklet是驱动程序实现可延迟函数的首选方法,tasklet建立在HI_SOFTIRT和TASKLET_SOFTIRQ两个软中断上。
原理
tasklet和高优先级的tasklet分别存放在tasklet_vec和tasklet_hi_vec数组中,二者都包括类型为tasklet_head的
NR_CPUS个元素,每一个元素都是指向tasklet描写叙述符链表的指针。
运行过程
HI_SOFTIRQ软中断相关的软中断函数是tasklet_hi_action(),而与TASKLET_SOFTIRQ相关的函数是tasklet_action()
1.禁止本地中断
2.获得本地CPU的逻辑号n
3.把tasklet_vec[n]或tasklet_hi_vec[n]所指向的链表的地址存入局部变量list
4.把tasklet_vec[n]或tasklet_hi_vec[n]的值赋为NULL,因此已调度的tasklet描写叙述符链表被清空
5.打开本地中断
6.对于list所指向的每一个tasklet描写叙述符
a.在多处理器系统上,检查tasklet的TASKLET_STATE_RUN标志。
if标志被设置,list又一次插入结构数组,并激活TASKLET_SOFTIRQ或HI_SOFTIRQ软中断,这个tasklet
被延迟
else 设置TASKLET_STATE_RUN标志,以便tasklet不能在其它CPU上执行
b.通过查看tasklet描写叙述符的count字段,看tasklet是否被禁止。假设是清TASKLET_STATE_RUN标志,把
list又一次插入结构数组,并激活对应的软中断。
c.假设tasklet被激活,清TASKLET_STATE_SCHED标志,并运行tasklet函数
编写一个设备驱动程序的步骤
1.分配一个新的tasklet_struct数据结构,并用tasklet_init()初始化它;
2.实现tasklet函数
3.禁止或使能tasklet
tasklet结构体
- struct tasklet_struct
- {
- struct tasklet_struct *next;
- unsigned long state;
- atomic_t count;
- void (*func)(unsigned long);
- unsigned long data;
- };
- tasklet结构变量是tasklet_vec链表的一个节点,next是链表的下一节点,state使用了两个位例如以下
- enum
- {
- TASKLET_STATE_SCHED, /* 1已经被调度,0表示还没调度*/
- TASKLET_STATE_RUN /* 1tasklet正在运行,0表示尚未运行,仅仅针对SMP有效,单处理器无意义 */
- };
- count用于禁止使能,每禁止一次计数加一,没使能一次计数减一,仅仅有禁止次数和使能次数一样(count等于0)时tasklet才会运行调用函数。
- func 运行函数不能有导致睡眠、不能堵塞的代码。
- data 运行函数的參数
tasklet的定义
- 定义时初始化
- 定义变量名为name的tasklets_struct变量,并初始化调用函数为func,參数为data,使能tasklet
- DECLARE_TASKLET(name, func, data); #define DECLARE_TASKLET(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
- 定义变量名为name的tasklets_struct变量,并初始化调用函数为func,參数为data,禁止tasklet
- DECLARE_TASKLET_DISABLED(name, func, data);
- #define DECLARE_TASKLET_DISABLED(name, func, data) \
- struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
- 执行中初始化 先定义 struct tasklet_struct name ;
- 后初始化
- 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的调用过程
- static inline void tasklet_schedule(struct tasklet_struct *t);使用此函数就可以完毕调用
- static inline void tasklet_schedule(struct tasklet_struct *t)
- {
- /*test_and_set_bit设置调度位TASKLET_STATE_SCHED,test_and_set_bit返回t->state设置前状态,假设设置前状态为1(已被调用)那么直接退出否则进入__tasklet_schedule函数*/
- if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
- __tasklet_schedule(t);
- }
- void fastcall __tasklet_schedule(struct tasklet_struct *t)
- {
- unsigned long flags;
- local_irq_save(flags); //关中断保存中断状态
- t->next = __get_cpu_var(tasklet_vec).list; //这两行用于将新插入的节点 放置在tasklet_vec链表的头部
- __get_cpu_var(tasklet_vec).list = t; //
- raise_softirq_irqoff(TASKLET_SOFTIRQ); //触发一个软终端
- local_irq_restore(flags); //使能中断的同一时候还恢复了由 local_irq_save() 所保存的中断状态
- }
- 至此调度函数已经触发了一个软中断,详细中断函数看tasklet的初始化
- void __init softirq_init(void)
- {
- open_softirq(TASKLET_SOFTIRQ, tasklet_action, NULL);//能够看到软中断触发后会运行tasklet_action这个函数
- open_softirq(HI_SOFTIRQ, tasklet_hi_action, NULL);
- }
- static void tasklet_action(struct softirq_action *a)
- {
- struct tasklet_struct *list;
- local_irq_disable(); //这里先关中断 保证原子操作
- list = __get_cpu_var(tasklet_vec).list; //取出tasklet_vec链表表头
- __get_cpu_var(tasklet_vec).list = NULL; //由于以下将会一次处理完,这里能够预先清空tasklet_vec链表,对于为处理完的会又一次增加链表
- //也能够实如今tasklet的处理函数中又一次增加自己。
- local_irq_enable();
- while (list) {
- struct tasklet_struct *t = list; //取一节点
- list = list->next; //循环遍历所有节点
- if (tasklet_trylock(t)) { //这里仅仅是測试TASKLET_STATE_RUN标记,防止tasklet反复调用
- //疑问:这里假设推断tasklet已经在上执行了,trylock失败,那么为什么后面会被又一次增加链表呢,那不是下次又执行了?
- if (!atomic_read(&t->count)) { //疑问: 假设tasklet被禁止了那么后面有把它加回链表中又一次触发一次软中断,这样不是一直有软中断了吗?为什么不在禁止的时候移出链表,使能时候在增加呢?
- if (!test_and_clear_bit(TASKLET_STATE_SCHED, &t->state)) //检查可调度位是否设置了,正常应该设置了的
- BUG();
- t->func(t->data); //处理调用函数
- tasklet_unlock(t); //清TASKLET_STATE_RUN标记
- continue;
- }
- tasklet_unlock(t);
- }
- local_irq_disable();
- t->next = __get_cpu_var(tasklet_vec).list; //对于trylock失败和tasklet禁止的节点会被又一次增加链表
- __get_cpu_var(tasklet_vec).list = t;
- __raise_softirq_irqoff(TASKLET_SOFTIRQ); //发起新的软中断,这里有两条链表一条是处理中的链表list,一个是当前tasklet_vec中的链表,当出现不能处理的节点时将节点又一次增加tasklet_vec中后发起新的软中断,那么未处理的节点也会在下次中断中处理。
- local_irq_enable();
- }
- }
相关函数
- /*和tasklet_disable类似,可是tasklet可能仍然执行在还有一个 CPU */
- static inline void tasklet_disable_nosync(struct tasklet_struct *t)
- {
- atomic_inc(&t->count); //降低计数后,t可能正在执行
- smp_mb__after_atomic_inc(); //保证在多处理器时同步
- }
- /*函数临时禁止给定的tasklet被tasklet_schedule调度,直到这个tasklet被再次被enable;若这个tasklet当前在执行, 这个函数忙等待直到这个tasklet退出*/
- static inline void tasklet_disable(struct tasklet_struct *t){
- tasklet_disable_nosync(t);
- tasklet_unlock_wait(t); //等待TASKLET——STATE_RUN标记清零
- smp_mb();
- }
- static inline int tasklet_trylock(struct tasklet_struct *t){
- return !test_and_set_bit(TASKLET_STATE_RUN, &(t)->state);
- }
- static inline void tasklet_unlock(struct tasklet_struct *t){
- smp_mb__before_clear_bit();
- clear_bit(TASKLET_STATE_RUN, &(t)->state);
- }
- static inline void tasklet_unlock_wait(struct tasklet_struct *t){
- while (test_bit(TASKLET_STATE_RUN, &(t)->state)) {
- barrier();
- }
- }
- /*使能一个之前被disable的tasklet;若这个tasklet已经被调度, 它会非常快执行。tasklet_enable和tasklet_disable必须匹配调用, 由于内核跟踪每一个tasklet的"禁止次数"*/
- static inline void tasklet_enable(struct tasklet_struct *t)
- {
- smp_mb__before_atomic_dec();
- atomic_dec(&t->count);
- }
- /*和tasklet_schedule类似,仅仅是在更高优先级执行。当软中断处理执行时, 它处理高优先级 tasklet 在其它软中断之前,仅仅有具有低响应周期要求的驱动才应使用这个函数, 可避免其它软件中断处理引入的附加周期*/
- void tasklet_hi_schedule(struct tasklet_struct *t);
- /*确保了 tasklet 不会被再次调度来执行,通常当一个设备正被关闭或者模块卸载时被调用。假设 tasklet 正在执行, 这个函数等待直到它执行完成。若 tasklet 又一次调度它自己,则必须阻止在调用 tasklet_kill 前它又一次调度它自己,如同使用 del_timer_sync*/
- void tasklet_kill(struct tasklet_struct *t)
- {
- if (in_interrupt())
- printk("Attempt to kill tasklet from interrupt\n");
- while (test_and_set_bit(TASKLET_STATE_SCHED, &t->state)) { //检測t是否被调度
- do
- yield();
- while (test_bit(TASKLET_STATE_SCHED, &t->state)); //等待t调度位清零,还未运行调用函数
- }
- tasklet_unlock_wait(t); //等待t调用函数运行完
- clear_bit(TASKLET_STATE_SCHED, &t->state); //函数调用完可能t被又一次增加链表,所以再清一次保证不再调用
- }
- 这个函数不是真的去杀掉被调度的tasklet,而是保证tasklet不再调用
linux下面的中断处理软件中断tasklet机制的更多相关文章
- 嵌入式Linux内核tasklet机制(附实测代码)
Linux 中断编程分为中断顶半部,中断底半部 中断顶半部: 做紧急,耗时短的事情,同时还启动中断底半部. 中断底半部: 做耗时的事件,这个事件在执行过程可以被中断. 中断底半部实现方法: taskl ...
- linux中的tasklet机制【转】
转自:http://blog.csdn.net/yasin_lee/article/details/12999099 转自: http://www.kerneltravel.net/?p=143 中断 ...
- Linux内核实现透视---软中断&Tasklet
软中断 首先明确一个概念软中断(不是软件中断int n).总来来说软中断就是内核在启动时为每一个内核创建了一个特殊的进程,这个进程会不停的poll检查是否有软中断需要执行,如果需要执行则调用注册的接口 ...
- 内核软中断之tasklet机制
1. 软中断IRQ简介 软中断(SoftIRQ)是内核提供的一种基于中断的延时机制, Linux内核定义的软中断有以下几种: enum { HI_SOFTIRQ=0, /*高优先级的tasklet*/ ...
- Linux 内核的文件 Cache 管理机制介绍
Linux 内核的文件 Cache 管理机制介绍 http://www.ibm.com/developerworks/cn/linux/l-cache/ 1 前言 自从诞生以来,Linux 就被不断完 ...
- Linux中断分层--软中断和tasklet
1. Linux中断分层 (1)上半部:当中断发生时,它进行相应的硬件读写,并“登记”该中断.通常由中断处理程序充当上半部.(一般情况下,上半部不可被打断) (2)下半部:在系统空闲的时候,对上半部“ ...
- Linux 内核中的 Device Mapper 机制
本文结合具体代码对 Linux 内核中的 device mapper 映射机制进行了介绍.Device mapper 是 Linux 2.6 内核中提供的一种从逻辑设备到物理设备的映射框架机制,在该机 ...
- linux上应用程序的执行机制
linux上应用程序的执行机制 执行文件是如何在shell中被"执行"的.本文中尽可能少用一些源码,免得太过于无 聊,主要讲清这个过程,感兴趣的同学可以去查看相应的源码了解更多的信 ...
- 理解Linux文档的默认安全机制、隐藏属性、特殊权限,妈妈在也不用担心你从删库到跑路!!!
写在前面 前面的章节 详解Linux文档属性.拥有者.群组.权限.差异,介绍了文档的基本权限,包括读写执行(r,w,x),还有文档若干的属性,包括是否为目录(d).文件(-).链接文件(l).拥有者. ...
随机推荐
- HTML5 CSS3 诱人的实例 : 网页载入进度条的实现,下载进度条等
今天给大家带来一个比較炫的进度条,进度条在一耗时操作上给用户一个比較好的体验,不会让用户认为在盲目等待,对于没有进度条的长时间等待,用户会任务死机了,毫不犹豫的关掉应用:一般用于下载任务,删除大量任务 ...
- Ubuntu下搭建ASP.NET 5
在Ubuntu下搭建ASP.NET 5开发环境 0x00 写在前面的废话 年底这段时间实在太忙了,各种事情都凑在这个时候,没时间去学习自己感兴趣的东西,所以博客也好就没写了.最近工作上有个小功能要 ...
- 开源Math.NET基础数学类库使用(05)C#解析Delimited Formats数据格式
原文:[原创]开源Math.NET基础数学类库使用(05)C#解析Delimited Formats数据格式 开源Math.NET基础数学类库使用系列文章总目录: 1.开源.NET基础数学计算组件 ...
- 高清电视产业的关键词,4K过渡时期8K未来
有些不尽人意,归根结底在于,绝大多数厂商并没有把电视的性能作为突破口,相反,仅仅是在外观.设计上做起了文章.在部分厂商看来,要真正研发性能一流的智能电视须要更高的投入,但改变一下外形似乎也能获 ...
- JVM学习(1)——通过实例总结Java虚拟机的运行机制(转)
俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及到的知识点总结如下: JVM的历史 JVM的运行流程简介 JVM的组成(基于 Java 7) JVM调优参数:-Xmx和-Xms ...
- 《Lua游戏开发实践指南》读后感
书籍地址:http://book.douban.com/subject/20392269/ 一句话点评该书:想用Lua作游戏脚本开发的同学值得一读! (一)本书特点 市面专门讲Lua的中文书籍非常少, ...
- hdu 4945 2048 (dp+组合的数目)
2048 Time Limit: 3000/1500 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others) Total Submi ...
- c# serialport读取不限数量的16进制数据
//private char[] HexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', ' ...
- 第七章——DMVs和DMFs(1)
原文:第七章--DMVs和DMFs(1) 简介: 从SQLServer2005开始,微软引入了一个名叫DMO(动态管理对象)的新特性,DMO可以分为DMFs(Dynamic Manage Functi ...
- 一个非常有用的函数——COALESCE
原文:一个非常有用的函数--COALESCE 很多人知道ISNULL函数,但是很少人知道Coalesce函数,人们会无意中使用到Coalesce函数,并且发现它比ISNULL更加强大,其实到目前为止, ...