中断下半部处理之tasklet
1.tasklet概述
下半部和退后执行的工作,软中断的使用只在那些执行频率很高和连续性要求很高的情况下才需要。在大多数情况下,为了控制一个寻常的硬件设备,tasklet机制都是实现自己下半部的最佳选择。其实tasklet是利用软中断实现的一种下半部机制。tasklet和软中断在本质上很相似,行为表现也相近。tasklet有两类中断代表:HI_SOFTIRQ和TASKLET_SOFTIRQ。这两者之间唯一的区别在于HI_SOFTIRQ类型的软中断先于TASKLET_SOFTIRQ类型的软中断执行。
2.tasklet的定义
tasklet由tasklet_struct结构表示,每个结构体单独代表一个tasklet,在<linux/interrupt.h>中定义为:
struct tasklet_struct
{
struct tasklet_struct *next;
unsigned long state;
atomic_t count;
void (*func)(unsigned long);
unsigned long data;
};
1.next:链表中的下一个tasklet;
2.state:tasklet的状态。有三个取值:0,TASKLET_STATE_SCHED和TASKLET_STATE_RUN之间的取值。TASKLET_STATE_SCHED表明tasklet已经被调度,正准备投入运行,TASKLET_STATE_RUN表示该tasklet正在运行。
3.count:tasklet的引用计数,如果它不为0,则tasklet被禁止,不允许执行;只有当它为0时,tasklet才被激活,并且在被设置为挂起(TASKLET_STATE_SCHED)状态时,该tasklet才能够被执行。
4.结构体中的func成员是tasklet的处理程序,data是它唯一的参数。
3.tasklet的调度
内核使用tasklet_schedule()函数来执行tasklet的调度,已调度的tasklet存放在两个单处理器数据结构:tasklet_vec(普通的tasklet)和tasklet_hi_vec(高优先级的tasklet)。这两个数据结构都是由tasklet_struct结构体构成的链表。链表中的每个tasklet_struct 代表一个不同的tasklet。
tasklet是由tasklet_schedule()和tasklet_hi_shedule()函数进行调度的,它们接受一个指向tasklet_struct结构的指针作为参数。两个函数非常相似(区别在于一个使用TASKLET_SOFTIRQ,一个使用HI_SOFTIRQ()),接下来我们来看下tasklet_schedule()的内核源码:
static inline void tasklet_schedule(struct tasklet_struct *t)
{
if (!test_and_set_bit(TASKLET_STATE_SCHED, &t->state))
__tasklet_schedule(t);
}
这个函数检查tasklet的状态,如果为TASKLET_STATE_SCHED,则表示已经被调度过了,直接返回。如果没有调度的话就会去执行__tasklet_schedule(t);
void __tasklet_schedule(struct tasklet_struct *t)
{
unsigned long flags;
local_irq_save(flags); //保存IF标志的状态,并禁用本地中断
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t; //下面的这两行代码就是为该tasklet分配per_cpu变量
__get_cpu_var(tasklet_vec).tail = &(t->next);
raise_softirq_irqoff(TASKLET_SOFTIRQ); //触发软中断,让其在下一次do_softirq()的时候,有机会被执行
local_irq_restore(flags); //恢复前面保存的标志
}
由上面的代码可以看出,每次中断只会向其中的一个cpu注册,而不是所有的cpu。完成注册后的tasklet由tasklet_action()函数来执行。
4.执行tasklet
前面说过tasklet被放在一个全局的tasklet_vec的链表中,链表中的元素是tasklet_struct结构体。内核中有个ksoftirqd()的内核线程,它会周期的遍历软中断的向量列表,如果发现哪个软中断向量被挂起了(pending),就执行相应的处理函数。tasklet对应的处理函数就是tasklet_action,这个函数在系统启动初始化软中断时,就在软中断向量表中注册。tasklet_action()遍历全局的tasklet_vec链表。链表中的元素为tasklet_struct结构体

static void tasklet_action(struct softirq_action *a)
{
struct tasklet_struct *list;
local_irq_disable();
list = __get_cpu_var(tasklet_vec).head; //得到当前处理器上的tasklet链表tasklet_vec或者tasklet_hi_vec.
__get_cpu_var(tasklet_vec).head = NULL; //将当前处理器上的该链表设置为NULL, 达到清空的效果。
__get_cpu_var(tasklet_vec).tail = &__get_cpu_var(tasklet_vec).head;
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);
tasklet_unlock(t);
continue;
}
tasklet_unlock(t);
}
local_irq_disable();
t->next = NULL;
*__get_cpu_var(tasklet_vec).tail = t;
__get_cpu_var(tasklet_vec).tail = &(t->next);
__raise_softirq_irqoff(TASKLET_SOFTIRQ);
local_irq_enable();
}
}
在遍历执行时,在tasklet_trylock()和tasklet_unlock()这一段函数中,完成的功能是:首先会去检查count值时否为0,前面已经分析过,当值不为0的时候,说明该tasklet被禁止,如果没有被禁止,则执行其注册的函数,首先会检查tasklet_state的标志位是否是TASKLET_STATE_RUN状态,如果是,则表示该任务已经在别的处理器上运行,如果没有运行,则将其状态标志设置为TASKLET_STATE_RUN,这样别的处理器就不会再去执行它了,这就保证了在同一时间里,相同类型的tasklet只能有一个在运行。
5.Tsklet提供的API
5.1.声明一个Tasklet
静态创建
声明一个tasklet,可以使用下面<linux/interrupt.h>中定义的两个宏中的一个:
#define DECLARE_TASKLET(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(0), func, data }
#define DECLARE_TASKLET_DISABLED(name, func, data) \
struct tasklet_struct name = { NULL, 0, ATOMIC_INIT(1), func, data }
这两个宏都能根据给定的名字静态的创建一个tasklet_struct结构。当该tasklet被调度后,给定的函数func会被执行,data为其参数。这两个宏的区别在于前者前面一个宏把创建的tasklet的引用计数器设置为0,该tasklet处于激活状态,另一个把引用计数器设置为1,所以该tasklet处于禁止状态。
动态创建
也可以使用一个间接的引用(一个指针)赋给一个动态创建的tasklet_struct结构的方式来初始化一个tasklet_init()函数:
tasklet_init(t, tasklet_handler, dev) 动态创建
5.2.编写tasklet处理函数
因为是软中断实现的,这就意味着不能在tasklet处理函数中使用信号量或者一些阻塞的函数。两个相同的tasklet绝不会同时执行,所以,如果你的tasklet和其他的tasklet或者是软中断共享了数据,你必须进行相应的锁保护。
5.3.调度
只有通过调度才能使我们的tasklet有机会被执行,这就使用上面提到的tasklet_shedule()函数。
tasklet_schedule(&my_tasklet);
5.4.禁止或者激活一个tasklet
static inline void tasklet_disable_nosync(struct tasklet_struct *t)
{
atomic_inc(&t->count);
smp_mb__after_atomic_inc();
}
可以用来禁止指定的tasklet,不过它无须再返回前等待tasklet执行完毕,这么做往往不太安全,因为你无法估计该tasklet是否仍在执行。
static inline void tasklet_disable(struct tasklet_struct *t)
{
tasklet_disable_nosync(t);
tasklet_unlock_wait(t);
smp_mb();
}
tasklet_disable()函数来禁止某个指定的tasklet,如果该tasklet当前正在执行,这个函数会等到它执行完毕再返回。
static inline void tasklet_enable(struct tasklet_struct *t)
{
smp_mb__before_atomic_dec();
atomic_dec(&t->count);
}
tasklet_enable()函数可以激活一个tasklet。
5.5.删除一个tasklet
通过调用tasklet_kill()函数从挂起的队列中去掉一个tasklet。该函数的参数是一个指向某个tasklet的tasklet_struct的指针。这个函数会等待tasklet执行完毕,然后再将它移除。该函数可能会引起休眠,所以要禁止在中断上下文中使用。
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)) {
do {
yield();
} while (test_bit(TASKLET_STATE_SCHED, &t->state));
}
tasklet_unlock_wait(t);
clear_bit(TASKLET_STATE_SCHED, &t->state);
}
6.编写自己的tasklet任务
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
char my_tasklet_data[]="my_tasklet_function was called";
/* Bottom Half Function */
void my_tasklet_function( unsigned long data )
{
printk( "%s\n", (char *)data );
}
//声明tasklet
DECLARE_TASKLET( my_tasklet, my_tasklet_function,(unsigned long) &my_tasklet_data );
int init_module( void )
{
/* Schedule the Bottom Half */
printk("diable my_tasklet\n");
tasklet_disable(&my_tasklet);
// printk("enable my_tasklet\n");
// tasklet_enable(&my_tasklet);
tasklet_schedule( &my_tasklet );
return 0;
}
void cleanup_module( void )
{
/* Stop the tasklet before we exit */
tasklet_kill( &my_tasklet );
return;
}
MODULE_AUTHOR("support@ingben.com");
MODULE_DESCRIPTION ("tasklet of buttom half test2");
MODULE_LICENSE ("GPL v2");
中断下半部处理之tasklet的更多相关文章
- Linux 中断下半部
为什么使用中断下半部? 中断执行的原则是要以最快的速度执行完,而且期间不能延时和休眠! 可是现实中,中断中可能没办法很快的处理完需要做的事,或者必须用到延时和休眠,因此引入了中断下半部. 中断中处理紧 ...
- Linux中断分层--软中断和tasklet
1. Linux中断分层 (1)上半部:当中断发生时,它进行相应的硬件读写,并“登记”该中断.通常由中断处理程序充当上半部.(一般情况下,上半部不可被打断) (2)下半部:在系统空闲的时候,对上半部“ ...
- 《Linux内核设计与实现》读书笔记(八)- 中断下半部的处理
在前一章也提到过,之所以中断会分成上下两部分,是由于中断对时限的要求非常高,需要尽快的响应硬件. 主要内容: 中断下半部处理 实现中断下半部的机制 总结中断下半部的实现 中断实现示例 1. 中断下半部 ...
- linux驱动学习笔记---实现中断下半部以及驱动编写规范(七)【转】
转自:https://blog.csdn.net/weixin_42471952/article/details/81609141 版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协 ...
- 中断下半部-tasklet
http://edsionte.com/techblog/ tasklet的实现 tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简单的.正如在前文中你所知道的那样,一个 ...
- 中断下半部tasklet【转】
本文转载自:http://edsionte.com/techblog/archives/1547 tasklet的实现 tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简 ...
- 【linux kernel】 中断处理-中断下半部
欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地址:http://www.cnblogs.com/embedded-tzp Csdn博客地址:http:// ...
- 【linux kernel】 中断处理-中断下半部【转】
转自:http://www.cnblogs.com/embedded-tzp/p/4453987.html 欢迎转载,转载时需保留作者信息,谢谢. 邮箱:tangzhongp@163.com 博客园地 ...
- Linux设备驱动程序 之 中断下半部
中断处理程序的局限 1. 中断处理程序以异步的方式执行,并且它有可能会打断其他重要代码的执行,因此,为了避免被打段的代码停止时间过长,中断处理程序应该执行的越快越好: 2. 如果当前有一个中断处理程序 ...
随机推荐
- Mego开发文档 - 数据属性生成值
数据属性生成值 该功能用于在数据插入或更新时为指定属性生成期望的值,Mego提供了非常灵活的实现方式以满足各种数据提交时的自动赋值问题. 生成值目的及模式 在Mego中生成值的目的一定是插入数据或更新 ...
- SpringBoot的配置文件加载顺序和使用方式
1.bootstrap.properties bootstrap.properties 配置文件是由"根"上下文优先加载,程序启动之初就感知 如:Spring Cloud Conf ...
- hadoop2.7.3+spark2.1.0+scala2.12.1环境搭建(4)SPARK 安装
hadoop2.7.3+spark2.1.0+scala2.12.1环境搭建(4)SPARK 安装 一.依赖文件安装 1.1 JDK 参见博文:http://www.cnblogs.com/liugh ...
- UVA732【DFS+栈】
题目:已知两个单词,利用一个栈,将第一个单词变成第二个单词,求出所有可能的操作序列. #include <stdio.h> #include<iostream> #includ ...
- 我做的python常用的小技巧
在python编码过程中,总会遇到各种各样的小问题,我想着记录下来,以备查用,总结过去,是为了更好的思考与进步. 一. 去除变量中(标题中)多余的字符 数据处理过程中,遇到这样的情况: y=['月份' ...
- Linux命令基础
开启Linux操作系统,root用户登录GNOME图形界面,如下图: 切换到虚拟终端2,使用普通用户身份登录,查看系统提示符,如下图: 使用命令退出虚拟终端2上的登录用户,如下图: 切换到虚拟终端5, ...
- linux查看日志文件内容命令tail、cat、tac、head、echo
linux查看日志文件内容命令tail.cat.tac.head.echo tail -f test.log你会看到屏幕不断有内容被打印出来. 这时候中断第一个进程Ctrl-C, ---------- ...
- JS面向对象使用面向对象进行开发
面向对象基础一之初体验使用面向对象进行开发 对 JS 中的面向对象的基础进行讲述, 初体验使用面向对象进行开发 主要内容是 面向对象的概念及特性 用面向对象的方式解决简单的标签创建实例 一些基础的 ...
- Linux OpenGL 实践篇-4 坐标系统
OpenGL中顶点经过顶点着色器后会变为标准设备坐标系.标准设备坐标系的各坐标的取值范围是[-1,1],超过这个范围的点将会被剔除.而这个变换的过程可描述为顶点在几个坐标系统的变换,这几个坐标系统为: ...
- springaop问题——Cannot subclass final class org.springframework.boot.autoconfigure.AutoConfigurationPackages$BasePackages
问题描述: 在使用springaop对目标对象增强时,若切点的条件过于宽泛就会出现以下异常! 如: @Before("execution(* *(..))") @Before(&q ...