内核编程 -- Workqueue

      该文章翻译自:http://www.tune2wizard.com/kernel-programming-workqueue/

      该文章的源代码测试环境是:Ubuntu-16.04,linux内核版本:4.2.1

      这篇文章主要讲的是工作队列(workqueue)相关的知识。首先我们讲一些workqueue的基础知识,然后会通过一个实际的例子讲解一下workqueue的应用。跟tasklet比较相似,都用在中断下半部分的处理。workqueue运行在内核线程的上下文,而tasklet运行在中断上下文,故workqueue允许睡眠的,而tasklet是不允许睡眠的;也由此可知,workqueue较tasklet有较大的延迟,对于那些有较高时间性能要求的不推荐使用workqueue。

一、workqueue的工作原理

       首先,将工作处理程序(处理函数)和工作项(work)关联起来;第二步,需要一个内核线程处理提交到工作队列上的函数;第三步,将工作项(work)提交到工作队列。

二、编码流程

       工作队列的结构体

 struct workqueue_struct;

     1、将工作处理程序(处理函数)和工作项(work)关联起来

#include <linux/workqueue.h>

DECLARE_WORK  (struct work_struct name,  void (*func)(void *));
或者用动态的方式来完成这项工作

INIT_WORK(struct work_struct *work, void (*function)(void *)

2、创建一个特定的线程,执行我们提交的工作(work)

     i)专用内核线程

struct workqueue_struct *create_workqueue(const char *name);

    ii)如果负载不是太高,我们可以使用普通的内核线程

struct workqueue_struct *create_singlethread_workqueue(const char *name);

3、提交工作项(work)到工作队列(workqueue)

     下面的这个API可以将工作线(work)提交到我们第2步创建的工作队列,函数执行成功将会返回0.

int queue_work(struct workqueue_struct *queue, struct work_struct *work);

     如果使用的是共享队列,使用下面的API可以很快的调用执行指定的工作项(work)

int schedule_work(struct work_struct *work);

4、清理工作队列使用下面的API:

void flush_scheduled_work(void);

5、一旦工作队列被创建,在cleanup()中使用下面的API销毁创建的工作队列

void destroy_workqueue (struct workqueue_struct * wq);

6、kmalloc() – 与用户控件的malloc()函数很相似. 但是kmalloc()从内核空间分配一定数量字节的空间,并且kmalloc分配的区域在物理上是连续的,下面API

#include <linux/slab.h>

#include <linux/gfp.h>

void * kmalloc(size_t size, int flags);
flags定义在 <linux/gfp.h>,它们分别是:

1. GFP_KERNEL – 当时用该标志时,如果申请不到内存,进程将会睡眠;内核为了使得该函数成功执行,会专门释放一些内存,所以该标志不适合使用在tasklet,中断等

2. GFP_ATOMIC – 该标志可以在中断执行路径中使用,该标志不会睡眠

3. GFP_USER – 为用户空间的页分配内存,可能会发生睡眠

4. GFP_HIGHUSER – 为用户空间分配高端内存

当使用了kmalloc函数分配了内存,我们可以使用下面的API释放

void kfree(const void *ptr);

container_of()函数的使用

parentPtr = container_of( container_field_pointer, container_type, container_field );
      该函数可以通过结构体中某个成员的地址来获得结构体的地址,我们举个例子在下面的代码解释一下,通过结构体成员&data来获得data所在结构体的地址:

#include <linux/kernel.h>

struct containerExample_t
{
	int data;
	char name[100];
};

containerExample_t *containerPtr = container_of(&data,struct containerExample_t,data);

三、代码部分:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>
#include <linux/slab.h>

MODULE_LICENSE("GPL");

/** 申明含有工作项(struct work_struct)的结构体 **/
typedef struct
{
	struct work_struct my_work;
	int x;
}my_work_t;

/** my_work_t 实例 **/
my_work_t *work;

/** 工作队列的处理函数 **/
static void ework_handler(struct work_struct *pwork);

//static void ework_handler2(struct work_struct *pwork);

/** Declare the workqueue struct **/
static struct workqueue_struct *eWq = 0;

/** 静态的将工作项与workqueue关联起来 */
/** eWorkqueue - 工作队列的名字, ework_handler - 工作队列的处理函数 **/
//static DECLARE_WORK(eWorkqueue, ework_handler2);
/*
static void ework_handler2(struct work_struct *pwork)
{
	printk("The ework_handler.2..called\r\n");
}
*/
static void ework_handler(struct work_struct *pwork)
{
	my_work_t *temp;
	printk("The ework_handler...called\r\n");

	/** pwork 是 my_work 的指针 **/
	temp = container_of(pwork,my_work_t,my_work);

	printk("The value of x is %d\r\n",temp->x);
}

static int eworkqueue_init(void)
{
	printk("Hello eWorkqueue !!! Welcome!!\r\n");
	printk("Create work struct object!!\r\n");
	work = (my_work_t *) kmalloc(sizeof(my_work_t), GFP_KERNEL);
	work->x = 1010;

	/** Init the work struct with the work handler **/
	INIT_WORK( &(work->my_work), ework_handler );

	if (!eWq)
	{
		printk("ewq..Single Thread created\r\n");
		eWq = create_singlethread_workqueue("eWorkqueue");
	}
	if (eWq)
	{
		printk("Push!! my work into the eWq--Queue Work..\r\n");
		queue_work(eWq, &(work->my_work) );
	}
	/** 如果我们想使用默认的内核线程,可以不用创建eWq,使用下面的函数即可**/
	/* schedule_work(); */

	return 0;
}

static void eworkqueue_exit(void)
{
	if (eWq)
		destroy_workqueue(eWq);
	kfree(work);
	printk("GoodBye..WorkQueue");
}

module_init(eworkqueue_init);
module_exit(eworkqueue_exit);

四、Makefile文件

obj-m := Workqueue.o

PWD := $(shell pwd)

KERNEL := /lib/modules/$(shell uname -r)/build

all:
	make -C $(KERNEL)  M=$(PWD) modules
clean:
	rm -rf *.o *~ core .*.cmd *.mod.c  *.order *.symvers  *.ko ./tmp_version

注:可能会遇到的错误:执行insmod之后会报invalid parameters类似的错误;通过命令dmesg | tail 查看后台输出的消息,如果提示workqueue:module is already loaded,那么我们可以把文件不要命名成workqueue.c就可以去除错误了。


总结:我们做的工作主要是,将要执行的函数ework_handler()绑定到工作项work->my_work,接着将工作项绑定到指定的工作队列

转载请注明出处:http://blog.csdn.net/jqwang1992/article/details/53887016


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值