13.1 内核工作队列

在linux中断编程中,需要中断程序分成中断顶部和中断底部两部分,顶部负责做中断标志,然后耗时的事情在中断底部执行。

那么底部分代码实现可以通过内核工作队列实现我们就必须先知道什么是内核工作对列。

工作队列(work queue)是另外一种将工作推后执行的形式,它和内核定时器推后的情况有所不同。工作队列可以把工作推后,交由一个内核线程去执行,也就是说,这个下半部分可以在进程上下文中执行。最重要的就是工作队列允许被重新调度甚至是睡眠。

工作、工作队列和工作者线程

如前所述,我们把推后执行的任务叫做工作(work),描述它的数据结构为work_struct,这些工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,而工作线程就是负责执行工作队列中的工作。系统默认的工作者线程,自己也可以创建自己的工作者线程。


13.1.1 工作结构

具体描述如下表:

名称

具体描述

定义文件

Workqueue.h (linux-3.5\include\Linux)

原型

struct work_struct {

atomic_long_t data;

struct list_head entry;

work_func_t func;/* 工作函数指针 */

#ifdef CONFIG_LOCKDEP

struct lockdep_map lockdep_map;

#endif

};

成员介绍

我们只需要关心一个成员函数:work_func_t func;

工作函数是一个函数指针,这个成员是指向工作函数的指针;

内核使用这个结构来描述一个工作,一个工作简单理解就是对应于一个函数,可以通过内核调度函数来调用work_struct中func指针所指向的函数

在内核中,一个工作(就是一段代码)就使用一个struct work_struct 结构表示。


13.1.2 工作服务函数

工作函数指针原型如下:

名称

具体描述

定义文件

Workqueue.h (linux-3.5\include\linux)

函数原型

typedef void (*work_func_t)(struct work_struct *work);

功能

是一个函数指针,编写一个工作的函数。

参数

struct work_struct *work

这个参数,指向struct work_struct结构变量本身。

示例

struct work_struct work;

INIT_WORK(&work, work_func);

这样初始化了一个work结构,work_func工作函数的参数就是指向work结构。

13.1.3初始化work结构的宏

1)初始化一个work结构

INIT_WORK(_work, _func)

_work: struct work_struct结构指针

_func:用来填充work_struct结构的fun成员,就是工作函数指针。

2)共享工作队列调度宏:

schedule_work(_work)

它也是一个宏,作用是调度一个工作_work。

_work:要调度工作的结构指针;

示例

schedule_work(&work)


13.1.4 使用工作队列步骤

1)定义一个工作结构变量

struct work_struct work;


2)初始化工作结构重点func成员)。

先编写一个工作函数

void work_func(structwork_struct * dat)

{

printk(“%p:”,dat);

……

}


初始化work:

INIT_WORK(&work, work_func);


  1. 在适当的地方调度工作

如果工作用于中断底部代码,则在中断顶部调度

schedule_work(&work);

不是马上执行,而是等待CPU空闲才执行work_func


13.1.5 自定义工作队列

内核有初始化好工作队列,因为工作队列在内核驱动中使用比较频繁,为了方便使用不用每人都去注册一个工作队列,所以内核提供了一个公共的工作队列。

那么什么时候我们要自己创建工作队列呢?

共享工作队列是每人都可以使用,那就意味着你不能一直霸占他去运行你的程序。

不过一般情况不可能运行那么多的代码,而且需要延迟时可以用睡眠这里简要介绍。

序号

接口函数

说明

1

create_workqueue

用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数:

@name:workqueue的名称

2

create_singlethread_workqueue

用于创建workqueue,只创建一个内核线程。输入参数:

@name:workqueue名称

3

destroy_workqueue

释放workqueue队列。输入参数:

@ workqueue_struct:需要释放的workqueue队列指针

4

queue_work

调度执行一个指定workqueue中的任务。输入参数:

@ workqueue_struct:指定的workqueue指针

@work_struct:具体任务对象指针

5

queue_delayed_work

延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。

注册函数中

struct workqueue_struct *wq=NULL;

创建自己的工作队列,加入工作到工作队列(加入内核就对其调度执行)*/

wq=create_workqueue("test");

INIT_WORK(&my_work,work_func);

queue_work(wq,&my_work);


注销函数中

/*工作队列销毁*/

destroy_workqueue(wq);

schedule_work函数也是调用queue_work再把工作队列参数默认添加成系统提供的工作队列。


13.1.6共享工作队列示例代码

使用内核共享工作队列,演示如何使用work参数。

#include <linux/module.h>

#include <linux/init.h>

#include <linux/workqueue.h>


typedef struct __mydat{

struct work_struct mywork;

int x;

int y;

int z;

} mydat_t;


//工作服务函数

static void work_handler(struct work_struct *data)

{

mydat_t *p;


//计算结构体首地址container_of(结构体某成员地址, 结构体, 结构体某成员)

//p = container_of(data, mydat_t, mywork);

//或使用下面一条代码

p = (mydat_t *)data;

printk(KERN_EMERG "data:%p,\n", data);

printk(KERN_EMERG "x:%d,\n", ((mydat_t *)data)->x);

printk(KERN_EMERG "y:%d,\n", ((mydat_t *)data)->y);

printk(KERN_EMERG "x:%d,\n", p->x);

printk(KERN_EMERG "y:%d,\n", p->y);

printk(KERN_EMERG "work handler function \n");

}


/* 初始化函数 */

static int __init test_init(void)

{

//struct work_struct work;

static mydat_t work;


work.x = 123;

work.y = 456;

printk(KERN_EMERG "&work:%p\n", &work);


//把work放置内核共享工作队列中

INIT_WORK(&work.mywork, work_handler);


//调用工作

schedule_work(&work.mywork);

printk("test_init \n");

return 0;

}


/* 卸载函数 */

static void __exit test_exit(void)

{

printk("%s is call\r\n", __FUNCTION__);

}


MODULE_LICENSE("GPL");

module_init(test_init);

module_exit(test_exit);

1.3.1.7 工作队列的其他函数

一些帮助函数用于清理或取消工作队列中的任务。想清理特定的任务项目并阻塞任务,直到任务完成为止,可以调用 flush_work 来实现。 指定工作队列中的所有任务能够通过调用 flush_workqueue 来完成。 这两种情形下,调用者阻塞直到操作完成为止。 为了清理内核全局工作队列,可调用 flush_scheduled_work。

int flush_work( struct work_struct *work );

int flush_workqueue( struct workqueue_struct *wq );

void flush_scheduled_work( void );

还没有在处理程序当中执行的任务可以被取消。调用 cancel_work_sync 将会终止队列中的任务或者阻塞任务直到回调结束(如果处理程序已经在处理该任务)。 如果任务被延迟,可以调用 cancel_delayed_work_sync。

int cancel_work_sync( struct work_struct *work );

int cancel_delayed_work_sync( struct delayed_work *dwork );

可以通过调用 work_pending 或者 delayed_work_pending 来确定任务项目是否在进行中。

#define work_pending(work) \

test_bit(WORK_STRUCT_PENDING_BIT, work_data_bits(work))


#define delayed_work_pending(w) \

work_pending(&(w)->work)


说明:返回值0表示成功执行 1表示未执行


13.1.8创建自定义工作队列示例代码

代码功能:创建自定义的工作队列(creator_workqueue)

#include <linux/kernel.h> //内核头文件

#include <linux/module.h> //模块

#include <linux/workqueue.h> /*工作队列相关*/


/*创建自己的工作队列相关*/

struct workqueue_struct *my_work_queue;


/*内核工作队列相关结构体*/

static struct work_struct my_work;


/*工作队列的处理函数*/

static void my_work_func(struct work_struct *work)

{

printk("\n\n工作队列调度成功!\n\n");

}


static int __init tiny4412_module_init(void)

{

/*创建自己的工作队列*/

my_work_queue =create_workqueue("my_workqueue");


/*初始化工作队列*/

INIT_WORK(&my_work,my_work_func);


/*调度工作队列*/

queue_work(my_work_queue,&my_work);

return 0;

}



static void __exit tiny4412_module_cleanup(void)

{


}


module_init(tiny4412_module_init); //驱动的入口,驱动安装的时候调用

module_exit(tiny4412_module_cleanup); //驱动的的出口,驱动卸载的时候调用

MODULE_LICENSE("GPL"); //驱动的许可证声

13.1.9 注销工作队列

void destroy_workqueue(struct workqueue_struct *wq)

工作队列不使用之后就直接注销。

13.1.10 等待工作队列执行完成

bool cancel_work_sync(struct work_struct *work)

取消工作队列执行,并等待工作队列执行完成。

示例:

cancel_work_sync(&ts->work);

destroy_workqueue(ts->queue);


13.2 内核延时工作队列

相同点:和上面的内核工作队列一样,可以实现中断的底部代码功能。

不同点:和内核工作队列惟一区别就是延后的时间可控制

13.2.1 关键数据结构:

延时工作队列结构:

struct delayed_work {

struct work_struct work; //工作结构

struct timer_list内核定时器结构

};


实际就是工作队列和内核定时器组成。

13.2.2 初始化

INIT_DELAYED_WORK(_work, _func)


它是内核定义的一个宏,定义在

功能:初始化一个delayed_work结构

参数:

_work: struct delayed_work *work结构指针,即传递的是指针

_func:用来填充struct delayed_work结构的fun指针

13.2.3 调度函数

内核提供了一个函数来调度延时工作队列。

头文件


函数原型

int schedule_delayed_work(struct delayed_work *dwork

unsigned long delay)

功能


参数

dwork:要调度的延时工作结构指针

delay:延后调度的时间,单位是时钟节拍。和内核定时器定时时间单位相同,但是不是到期时间,而是定时时间。

返回值



13.2.4 应用延时工作队列步骤

1)定义一个struct delayed_work结构变量

2)编写一个延时工作函数。

3)初始化struct delayed_work结构

3)在适当地方调用工作。

13.2.5 延时工作队列示例代码

#include <linux/kernel.h> //内核头文件

#include <linux/module.h> //模块

#include <linux/workqueue.h> /*工作队列相关*/


/*延时工作队列相关结构体*/

static struct delayed_work my_delay_work;


/*工作队列的处理函数*/

static void my_work_func(struct work_struct *work)

{

printk("\n\n延时系统共享工作队列调度成功\n");

}


static int __init tiny4412_module_init(void)

{

/*初始化延时工作队列*/

INIT_DELAYED_WORK(&my_delay_work,my_work_func);


/*添加延时工作到系统工作队列中等待执行*/

schedule_delayed_work(&my_delay_work,HZ*5);

return 0;

}



static void __exit tiny4412_module_cleanup(void)

{


}


module_init(tiny4412_module_init); //驱动的入口,驱动安装的时候调用

module_exit(tiny4412_module_cleanup); //驱动的的出口,驱动卸载的时候调用

MODULE_LICENSE("GPL"); //驱动的许可证声