Linux内核机制:工作队列

一、应用场景

工作队列一般用来做滞后的工作,比如在中断里面要做很多事,但是比较耗时,这时就可以把耗时的工作放到工作队列。说白了就是系统延时调度的一个自定义函数。把推后执行的任务叫做工作,描述它的数据结构为work_struct ,这些工作以队列结构组织成工作队列,其数据结构为workqueue_struct ,而工作线程就是负责执行工作队列中的工作。系统默认的工作者线程为events
工作队列是另外一种将工作推后执行的形式。工作队列可以把工作推后,交由一个内核线程去执行这个下半部分总是会在进程上下文执行,但由于是内核线程,其不能访问用户空间。最重要特点的就是工作队列允许重新调度甚至是睡眠。通常,在工作队列和软中断/tasklet中作出选择非常容易。可使用以下规则:
如果推后执行的任务需要睡眠,那么只能选择工作队列;
如果推后执行的任务需要延时指定的时间再触发,那么使用工作队列,因为其可以利用timer延时;
如果推后执行的任务需要在一个tick之内处理,则使用软中断或tasklet,因为其可以抢占普通进程和内核线程;
如果推后执行的任务对延迟的时间没有任何要求,则使用工作队列,此时通常为无关紧要的任务。
实际上,工作队列的本质就是将工作交给内核线程处理,因此其可以用内核线程替换。但是内核线程的创建和销毁对编程者的要求较高,而工作队列实现了内核线程的封装,不易出错,所以我们也推荐使用工作队列。

二、常用接口

2.1  常用结构体

struct work_struct {
             unsigned long pending;
             struct list_headentry;                 
             void (*func)(void*);//该工作的处理函数                  
             void*data;//该工作处理函数的参数                                 
             void*wq_data;                          
             strut timer_listtimer;            
};

struct workqueue_struct {
	unsigned int		flags;		/* W: WQ_* flags */
	union {
		struct cpu_workqueue_struct __percpu	*pcpu;
		struct cpu_workqueue_struct		*single;
		unsigned long				v;
	} cpu_wq;				/* I: cwq's */
	struct list_head	list;		//该工作队列所管理的所有工作

	struct mutex		flush_mutex;	/* protects wq flushing */
	int			work_color;	/* F: current work color */
	int			flush_color;	/* F: current flush color */
	atomic_t		nr_cwqs_to_flush; /* flush in progress */
	struct wq_flusher	*first_flusher;	/* F: first flusher */
	struct list_head	flusher_queue;	/* F: flush waiters */
	struct list_head	flusher_overflow; /* F: flush overflow list */

	mayday_mask_t		mayday_mask;	/* cpus requesting rescue */
	struct worker		*rescuer;	/* I: rescue worker */

	int			nr_drainers;	/* W: drain in progress */
	int			saved_max_active; /* W: saved cwq max_active */
#ifdef CONFIG_LOCKDEP
	struct lockdep_map	lockdep_map;
#endif
	char			name[];		/* I: workqueue name */
};

2.2 创建工作队列

create_workqueue(name);//为一个工作队列,在每个cpu上都创建一个线程
struct workqueue_struct *create_singlethread_workqueue(const char *name);//为一个工作队列,在某个cpu上创建线程
void destroy_workqueue( struct workqueue_struct *name);
一个工作队列必须明确的在使用前创建,若使用  create_workqueue,  就得到一个工作队列它在系统的每个处理器上有一个专用的线程。在很多情况下,过多线程对系统性能有影响,如果单个线程就足够则使用  create_singlethread_workqueue  来创建工作队列。内核也为每个 CPU 创建了默认的工作线程,如果用户的任务不是很大,可以优先考虑将工作加入到内核默认的工作队列中进行处理,该工作队列的名字为 keventd_wq ,处理它的内核线程为 event/x

2.3 初始化用户工作

当工作队列创建好了后,用户就可以向它里面添加要执行的工作work_struct,系统定义了很多初始化宏来定义用户工作。

在代码中动态定义work_struct:
INIT_WORK(work, func );
INIT_DELAYED_WORK(work, func );
INIT_DELAYED_WORK_DEFERRABLE(work, func );
在代码中静态定义work_struct:
DECLARE_WORK(n, f);
DECLARE_DELAYED_WORK(n, f);
DECLARE_DEFERRED_WORK(n, f);

2.4 加入工作队列
当用户创建好了自己的工作后,就可以将它加入到用户创建的工作队列或者内核默认的工作队列中去了。

用户自定义工作队列:
int queue_work(struct workqueue_struct *wq, struct work_struct *work );//将work这个工作加入到wq工作队列中,将马上启动wq关联的线程去处理里面的工作
int queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work );//指定那个CPU上的工作线程去处理
int queue_delayed_work( struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay );//延时delay才启动wq的处理线程去处理工作
int queue_delayed_work_on( int cpu, struct workqueue_struct *wq,struct delayed_work *dwork, unsigned long delay );

内核默认工作队列:
int schedule_work(struct work_struct *work );//加入到系统默认的工作队列,马上启动默认它的线程处理工作
int schedule_work_on(int cpu, struct work_struct *work );
int scheduled_delayed_work( struct delayed_work *dwork, unsigned long delay );
int scheduled_delayed_work_on(int cpu, struct delayed_work *dwork, unsigned long delay );

2.5 取消队列中任务

bool cancel_work_sync(struct work_struct *work)取消一个工作,至到它运行结束
bool cancel_delayed_work_sync(struct delayed_work *dwork)取消一个延时工作,至到它运行结束

void flush_workqueue(struct workqueue_struct *wq)//清除工作队列中的工作----阻塞执行里面的工作,至到全部工作完成,然后将它们从工作队列中清除
bool flush_work(struct work_struct *work)//清除一个任务----阻塞执行任务,直到任务结束,然后将该工作从工作队列中清除
bool flush_delayed_work(struct delayed_work *dwork)//清除一个delay任务----阻塞执行任务,直到任务结束,然后将该工作从工作队列中清除

三、应用举例

#include<linux/module.h>
#include<linux/init.h>
#include<linux/time.h>
#include<linux/workqueue.h>
#include<linux/slab.h>

static struct workqueue_struct *queue=NULL;
struct my_work
{	
	struct delayed_work work;
	char *data;	
};

static struct my_work  *mywork;
static void work_handler(struct work_struct *data)
{
	printk(KERN_ALERT "hello, I am a work_struct's function.\n" );
	struct my_work *work =(struct my_work *)container_of(data, struct my_work, work);
	printk(KERN_ALERT "the work data is :%s",work->data);
}

static int __init test_init(void)
{
	queue=create_singlethread_workqueue("my_work_queue"); /*创建一个单线程的工作队列*/
	if(!queue)
		goto err;
  char string[]="ni hao,hehe!";
	mywork=(struct my_work *)kzalloc(sizeof(struct my_work),GFP_KERNEL);
	mywork->data=(char *)kzalloc(strlen(string)+1,GFP_KERNEL);
	strcpy(mywork->data,string);
	INIT_DELAYED_WORK(&(mywork->work),work_handler);

 queue_delayed_work(queue,&(mywork->work),msecs_to_jiffies(1000*10));//10s后执行work的func
		return 0;
err:
		return -1;
}

static void __exit test_exit(void)
{
		destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);

参考博文:

http://tanatseng.blog.163.com/blog/static/174991629201132734828701/

http://blog.csdn.net/tommy_wxie/article/details/7204306

http://blog.chinaunix.net/uid-25014876-id-100700.html


你可能感兴趣的:(linux内核,工作队列)