内核微线程  ---- tasklet和工作队列一样,可以用于推后执行的代码。tasklet(小任务)机制是中断处理下半部分最常用的一种方法,其使用也是非常简单的。一个使用tasklet的中断程序首先会通过执行中断处理程序来快速完成上半部分的工作,接着通过调用tasklet使得下半部分的工作得以完成。在中断处理程序中,除了完成对中断的响应等工作,还要调用tasklet。

1、 数据结构

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处理函数的参数     
};

count :  引用计数器,值为0,tasklet 才可以调度(调度结果就是 func被执行)

2. API 函数

动态初始化函数:

void tasklet_init(struct tasklet_struct *t,void (*func)(unsigned long), unsigned long 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)静态初始化

静态初始化DECLARE_TASKLET(name, func, data)

定义一个名字为 name 的结构变量 ,并且使用 func,data对结构进行初始化,这个宏定义的 tasklet 是可调度的。

静态初始化DECLARE_TASKLET_DISABLED(name, func, data)和DECLARE_TASKLET(name, func, data),不同是它开始不能被调度。必须先把 count 设置为0,才可以调度

name:struct tasklet_struct的名字

func:tasklet函数指针

data:传递给func函数的参数

3)激活/取消激活 tasklet  

void tasklet_disable(struct tasklet_struct *t)    --把 count 设置为1

void tasklet_enable (struct tasklet_struct *t)    --把count 设置为0

4)小任务调度函数

void  tasklet_schedule (struct tasklet_struct *t)调度某个指定的tasklet小任务,调用后tasklet关联的函数会执行.一旦执行,则会在适当时候去执行 tasklet_struct 绑定的函数。

对同一个 struct tasklet_struct 连续调度多次,效果等同一次(前提条件:当前一次调用,绑定函数还没有执行)。

5)小任务 杀死函数(取消任务)

tasklet_kill(struct tasklet_struct *t);

6) tasklet和普通工作队列区别:

它所绑定的函数不能休眠

它的响应速度高于普通工作队列。


tasklet 微线程的编程步骤:

taskle 内核机制实现过程是非常复杂的,但是对于驱动开发者来说,重点是掌握如果使用内核已经给我们实现好的tasklet机制。tasklet编程其实只有简单的几步,下面我们总结一下tasklet机制的编程步骤。

1. 定义tasklet 工作函数

void my_tasklet_function(unsigned long data)

{

  printk(KERN_EMERG "%s\r\n",(char *)data);

  udelay(2000);     //不是休眠,而软件延时

}

2. 定义tasklet 结构变量

定义分有静态定义和动态定义两种方式:

动态定义:

struct tasklet_struct my_tasklet;

静态定义:

DECLARE_TASKLET(my_tasklet, my_tasklet_function, data);

 3. 初始化tasklet结构,绑定工作函数

如果上一步是采用静态定义,则这一步不用再做,跳过。如果是采用动态定义tasklet,则使用tasklet_init()函数进行初始化以及绑定。

tasklet_init(&my_tasklet, my_tasklet_function, data)

4. 在适当的地方调度工作函数

tasklet一般是用于处理中断的下半部的,所以一般在中断的上半部调度tasklet工作函数。

tasklet_schedule(&my_tasklet);

5. 销毁tasklet工作任务

在确定不再使用tasklet时,应该在适当的地方调用tasklet_kill()函数销毁tasklet任务,释放资源,这个适当的地方一般的tasklet初始化地方是相反的,比如,如果是在模块初始化函数初始化了tasklet,则相应地是在模块卸载函数调用tasklet_kill函数来销毁tasklet任务。

tasklet_kill(&my_tasklet);


tasklet的简单示例:

               本例子程序并没有加上中断相关代码,仅作为学习tasklet机制编程示例,如果要用于中断处理中,只需要把tasklet_schedule(&my_tasklet)放到中断函数中,把中断在耗时的代码写在my_tasklet_function()函数中即可。

#include<linux/kernel.h>
#include<linux/module.h>
#include<linux/interrupt.h>
#include <linux/delay.h>
void my_tasklet_function(unsigned long data);
char my_tasklet_data[]="my_tasklet_function was  called.";


/*静态定义并初始化一个tasklet 内核微线程*/
DECLARE_TASKLET(my_tasklet,my_tasklet_function,(unsigned long)my_tasklet_data);
/*tasklet任务函数*/
void my_tasklet_function(unsigned long data)
{
  printk(KERN_EMERG "%s\r\n",(char *)data);
  udelay(2000);	
tasklet_schedule(&my_tasklet);
}

int __init init_test(void)
{
	printk(KERN_EMERG "mondule insmod\r\n");

	/*调度tasklet*/
	tasklet_schedule(&my_tasklet);
	
	return 0;
}

void __exit exit_test(void)
{
	 printk(KERN_EMERG "mondule remove\r\n");
	/* 销毁tasklet */
	tasklet_kill(&my_tasklet);
}

module_init(init_test);   

module_exit(exit_test);  

MODULE_LICENSE("GPL");

 

Logo

更多推荐