中断下半部处理之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. 如果当前有一个中断处理程序 ...
随机推荐
- 201621123057 《Java程序设计》第6周学习总结
1. 本周学习总结 1.1 面向对象学习暂告一段落,请使用思维导图,以封装.继承.多态为核心概念画一张思维导图或相关笔记,对面向对象思想进行一个总结. 注1:关键词与内容不求多,但概念之间的联系要清晰 ...
- Linux下Apache服务的查看和启动
cd到/etc/rc.d/init.d/目录,并列出该目录下的所有文件,看看是否有httpd 使用httpd -v查看已经安装的httpd的版本 使用rpm -qa | grep http ...
- 自主学习之RxSwift(二) -----flatMap
最近项目中有这么一个需求,下面是三个网络请求 A.从服务器获取到时间戳(GET 方法,获取 timeLine) B.进行用户头像上传,获得回传的URL(POST方法,参数为 userId, timeL ...
- Hibernate之深入持久化对象
Hibernate是一个彻底的O/R Mapping 框架.之所以说彻底,是因为相对于其他的 框架 ,如Spring JDBC,iBatis 需要手动的管理SQL语句,Hibernate采用了完全 面 ...
- Angular组件——组件生命周期(一)
组件声明周期以及angular的变化发现机制 红色方法只执行一次. 变更检测执行的绿色方法和和组件初始化阶段执行的绿色方法是一个方法. 总共9个方法. 每个钩子都是@angular/core库里定义的 ...
- JAVA_SE基础——54.异常
异常的体系: ----------| Throwable 所以异常或者错误类的超类 --------------|Error 错误 错误一般是用于jvm或者是硬件引发的问题,所以我们一般不会通 ...
- jsMath对象
Math对象: abs.用来求绝对值. ceil:用来向上取整. floor:用来向下取整. round:用来四舍五入取近似值. sqrt:用来开方. pow:括号内有2位参数.如pow(2,5)表示 ...
- Python 爬虫性能相关
性能相关 在编写爬虫时,性能的消耗主要在IO请求中,当单进程单线程模式下请求URL时必然会引起等待,从而使得请求整体变慢. import requests def fetch_async(url): ...
- 电脑上的安卓系统——PhoenixOS浅度体验
前言 其实这篇关于PhoenixOS的浅度评测在几个月前就准备发了,当时是刚看到新闻说Android 7.0 x86的正式版刚刚发布,于是就下载来安装一番,结果.....体验极差= =,只能用这4个字 ...
- 创建类似于Oracle中SYS_GUID() 的方法
CREATE or REPLACE FUNCTION "sys_guid"()RETURNS "pg_catalog"."varchar" ...