业无高卑志当坚,男儿有求安得闲?这篇文章主要讲述#导入Word文档图片# Linux下内核微线程tasklet相关的知识,希望能为你提供帮助。
内核微线程和内核定时器,共享工作队列一样,也可以用于推后执行某些代码。?
tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简单的。?
Tasklet内核的实现机制和内核定时器差不多,也是软中断实现的。所以服务函数也是运行在中断上下文,需要注意一些使用细节。?
一个使用tasklet的中断程序首先会通过执行中断处理程序来快速完成上半部分的工作,接着通过调用tasklet使得下半部分的工作得以完成。?
可以看到,下半部分被上半部分所调用,至于下半部分何时执行则属于内核的工作。对应到我们此刻所说的tasklet就是,在中断处理程序中,除了完成对中断的响应等工作,还要调用tasklet,如下图示。?
一、tasklet核心结构?这个结构定义在Interrupt.h (linux-3.5\\include\\linux)下,如下:?
struct tasklet_struct ?
struct tasklet_struct *next;
//链表中的下一个tasklet?
unsigned long state;
//tasklet的状态 ?
atomic_t count;
引用计数器?
void(*func) (unsigned long data);
//tasklet处理函数 --参数就下面的data ?
unsigned long data;
//给tasklet处理函数的参数 ?
;?
在这个结构体中:?
第一个成员代表链表中的下一个tasklet。?
第二个变量代表此刻tasklet的状态。?
一般为TASKLET_STATE_SCHED,表示此tasklet已被调度且正准备运行;?
此变量还可取TASKLET_STATE_RUN,表示正在运行,但只用在多处理器的情况下。?
count成员是一个引用计数器,只有当其值为0时候,tasklet才会被激活;否则被禁止,不能被执行。?
而接下来的func变量很明显是一个函数指针,它指向tasklet处理函数,这个处理函数的唯一参数为data。?
二、内核提供编程的API 函数?、初始化?
这里也可以进行动态初始化和静态初始化。?
2.1.1、动态初始化?
动态初始化函数如下:?
头文件?
|
|
原型?
| void tasklet_init(struct tasklet_struct *t,?
void (*func)(unsigned long), ?
unsigned long data)?
|
函数功能?
| 动态初始化结构体,将引用计数器count=0,和状态state=0。?
示例:?
struct tasklet_struct my_tasklet;
?
tasklet_init(&
my_tasklet,myfunc,data);
//动态初始化?
功能等效于:?
DECLARE_TASKLET (my_tasklet, myfunc, data)静态初始化?
|
参数?
| t: struct tasklet_struct 结构指针。?
func:小任务函数。?
data:传递给工作函数的实际参数。?
|
使用示例:?
char p[]="hello tasklet!"?
struct tasklet_struct t;?
void t_func(unsigned long data)?
char *p = (char*) data;
?
printk("%s",p);
?
..... ?
?
//初始化函数中?
tasklet_init(&
t,t_func, (unsigned long )p);
?
2.1.2、静态初始化?
静态初始化使用的是一个宏,该宏定义在Interrupt.h (linux-3.5\\include\\linux)。?
DECLARE_TASKLET(name, func, data)?
定义一个名字为 name 的struct tasklet_struct结构变量。?
并且使用 func,data对结构进行初始化,这个宏定义的 tasklet 是可调度的。?
还有以下静态初始化的宏:?
DECLARE_TASKLET_DISABLED(name, func, data)?
功能和DECLARE_TASKLET(name, func, data)。?
不同的是它开始不能被调度。必须先把count 设置为0,才可以调度。 ?
宏参数:?
name:struct tasklet_struct的名字?
func:tasklet函数指针?
data:传递给func函数的参数?
2.2、激活/取消激活 tasklet?
和tasklet调度相关的核心 api有:?
tasklet_disable,?
tasklet_enable,?
tasklet_schedule。?
如果一个小任务状态是被禁止的,要调度则应该先调用tasklet_enable函数,如果要禁止小任务则可以调用tasklet_disable函数。?
- void tasklet_disable(struct tasklet_struct *t)
函数原型?
| static inline void tasklet_disable(struct tasklet_struct *t)?
|
函数功能?
| 禁止某个指定的tasklet小任务,禁止后这个小任务不能被调度。?
这个实际是把count值加1.?
|
函数参数?
| t:tasklet结构指针?
示例:?
tasklet_disable (&
my_tasklet)?
|
函数返回值?
| 无?
|
函数头文件?
| include\\linux\\ interrupt.h?
|
函数定义文件?
| include\\linux\\ interrupt.h?
|
- void tasklet_enable (struct tasklet_struct *t)?
函数原型?
| static inline void tasklet_enable (struct tasklet_struct *t)?
|
函数功能?
| 使能某个指定的tasklet小任务,这样才能调度?
这个实际是把count值减1.?
|
函数参数?
| t:tasklet结构指针?
tasklet_enable(&
my_tasklet)?
|
函数返回值?
| 无?
|
函数头文件?
| include\\linux\\interrupt.h?
|
函数定义文件?
| include\\linux\\interrupt.h?
|
2.3、小任务调度函数?
头文件?
| Interrupt.h (linux-3.5\\include\\linux)?
|
原型?
| void tasklet_schedule (struct tasklet_struct *t)?
|
功能?
| 调度某个指定的tasklet小任务,调用后tasklet关联的函数会执行。?
一旦执行,则会在适当时候去执行 tasklet_struct 绑定的函数。?
|
参数?
| 需要调度的struct tasklet_struct结构指针。?
|
对同一个 struct tasklet_struct 连续调度多次,效果等同一次(前提条件:当前一次调用,绑定函数还没有执行)。?
tasklet_schedule(&
my_tasklet);
?
tasklet_schedule(&
my_tasklet);
?
tasklet_schedule(&
my_tasklet);
?
这样只会执行一次绑定的函数。?
2.4、微线程 kill 函数(取消任务)?
函数原型?
| void tasklet_kill(struct tasklet_struct *t)?
|
函数功能?
| 将一个已经被调度了的tasklet杀死,即将其恢复到未调度的状态?
|
函数参数?
| t:tasklet结构指针?
|
函数返回值?
| 无?
|
函数头文件?
| include\\linux\\interrupt.h?
|
函数定义文件?
| kernel\\softirq.c ?
|
该函数确保指定的tasklet不会被再次调度运行;当设备要被关闭或者模块要被移除时,我们通常调用这个函数。?
如果tasklet正被调度执行,该函数会等待其退出。?
如果tasklet重新调度自己,则应该避免在调用tasklet_kill之前完成重新调度。?
(注意:不能在tasklet的处理函数中调用这个函数,否则会死锁)。?
三、tasklet 微线程的编程步骤?taskle 内核机制实现过程是非常复杂的,但是对于驱动开发者来说,重点是掌握如果使用内核已经给我们实现好的tasklet机制。tasklet编程其实只有简单的几步,下面我们总结一下tasklet机制的编程步骤。?
3.1、定义tasklet 工作函数?
void my_tasklet_function(unsigned long data)?
?
printk(KERN_EMERG "%s\\r\\n",(char *)data);
?
。。。?
?
3.2、定义tasklet 结构变量?
定义分有静态定义和动态定义两种方式:?
1)动态定义:?
struct tasklet_struct my_tasklet;
?
2)静态定义:?
DECLARE_TASKLET(my_tasklet, my_tasklet_function, data);
?
3.3、初始化tasklet结构,绑定工作函数?
如果上一步是采用静态定义,则这一步不用再做,跳过。如果是采用动态定义tasklet,则使用tasklet_init()函数进行初始化以及绑定。?
tasklet_init(&
my_tasklet, my_tasklet_function, data)?
3.4、在适当的地方调度工作函数?
tasklet一般是用于处理中断的下半部的,所以一般在中断的上半部调度tasklet工作函数。?
tasklet_schedule(&
my_tasklet);
?
3.5、销毁tasklet工作任务?
在确定不再使用tasklet时,应该在适当的地方调用tasklet_kill()函数销毁tasklet任务,释放资源,这个适当的地方一般的tasklet初始化地方是相反的,比如,如果是在模块初始化函数初始化了tasklet,则相应地是在模块卸载函数调用tasklet_kill函数来销毁tasklet任务。?
【#导入Word文档图片# Linux下内核微线程tasklet】tasklet_kill(&
my_tasklet);
?
推荐阅读