内核编程 -- Workqueue

来源:互联网 发布:怎样在淘宝卖二手书 编辑:程序博客网 时间:2024/06/15 03:31

      该文章翻译自: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.oPWD := $(shell pwd)KERNEL := /lib/modules/$(shell uname -r)/buildall:make -C $(KERNEL)  M=$(PWD) modulesclean: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


0 0
原创粉丝点击