分类:
2010-12-31 15:56:06
1,先看看工作队列和tasklet的区别:
(1) 定时器和tasklet:
Tasklets resemble kernel timers in 3
ways.
1)They are always run at interrupt time,
2)they
always run on the same CPU that schedules them,
3)and they
receive an unsigned long argument. Unlike kernel timers, however, you
can’t ask to execute the function at a specific time.
(2) tasklet和工作队列:
Workqueues are, superficially, similar
to tasklets; they allow kernel code to request that a function be
called at some future time. There are, however, some
significant differences between the two, including:
1)
Tasklets run in software interrupt context with the result that all
tasklet code must be atomic. Instead, workqueue functions run in the
context of a special kernel process; as a result, they have more
flexibility. In particular, workqueue functions can sleep.
2)Tasklets always run on the processor from which they were originally
submitted. Workqueues work in the same way, by default.
3)Kernel code can request that the execution of workqueue functions be
delayed for an explicit interval.
2,工作队列(work queue)是Linux
kernel中将工作推后执行的一种机制。这种机制和BH或Tasklets不同之处在于工作队列是把推后的工作交由一个内核线程去执行,因此工作队列的
优势就在于它允许重新调度甚至睡眠。工作队列是2.6内核开始引入的机制,在2.6.20之后,工作队列的数据结构发生了一些变化,因此本文分成两个部分
对2.6.20之前和之后的版本分别做介绍。
(1)2.6.0~2.6.19
数据结构:
struct
work_struct {
unsigned long pending;
struct list_head
entry;
void (*func)(void *);
void *data;
void
*wq_data;
struct timer_list timer;
};
pending是用来记录工作是否已经挂在队
列上;entry是循环链表结构;func作为函数指针,由用户实现;data用来存储用户的私人数据,此数据即是func的参数;wq_data一般用
来指向工作者线程(工作者线程参考下文);timer是推后执行的定时器。work_struct的这些变量里,func和data是用户使用的,其他是
内部变量,我们可以不用太过关心。
API:
1) INIT_WORK(_work, _func, _data)
初始化指定工
作,目的是把用户指定的函数_func及_func需要的参数_data赋给work_struct的func及data变量。
2) int
schedule_work(struct work_struct *work)
对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列
和工作者线程。工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为“events”的工作者线程,当调用
schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。
3) int
schedule_delayed_work(struct work_struct *work, unsigned long delay)
延
迟执行工作,与schedule_work类似。
4) void flush_scheduled_work(void)
刷新缺省工作队
列。此函数会一直等待,直到队列中的所有工作都被执行。
5) int cancel_delayed_work(struct
work_struct *work)
flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调
用cancel_delayed_work。
以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工
作队列。调用如下的API:
1) struct workqueue_struct *create_workqueue(const char
*name)
创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。
2) int queue_work(struct
workqueue_struct *wq, struct work_struct *work)
类似于schedule_work,区别在
于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。
3) int queue_delayed_work(struct
workqueue_struct *wq, struct work_struct *work, unsigned long delay)
延
迟执行工作。
4) void flush_workqueue(struct workqueue_struct *wq)
刷新指定工作
队列。
5) void destroy_workqueue(struct workqueue_struct *wq)
释放创建的工作
队列。
下面一段代码可以看作一个简单的实作:
void my_func(void *data)
{
char *name = (char *)data;
printk(KERN_INFO “Hello world, my
name is %s!\n”, name);
}
struct workqueue_struct *my_wq =
create_workqueue(“my wq”); //创建队列
struct work_struct
my_work;
INIT_WORK(&my_work,
my_func, “Jack”); //创建工作
queue_work(my_wq,
&my_work);
//添加工作到工作队列
destroy_workqueue(my_wq);
(2)2.6.20~2.6.??
自2.6.20起,工作队列的数据结构发生了一些变化,使用时不能沿用旧的方法。
数据结
构:
typedef void (*work_func_t)(struct work_struct *work);
struct
work_struct {
atomic_long_t data;
struct list_head entry;
work_func_t func;
};
与2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前
的版本相同,func和data发生了变化,另外并无其他的变量。entry我们不去过问,这个和以前的版本完全相同。data的类型是
atomic_long_t,这个类型从字面上看可以知道是一个原子类型。第一次看到这个变量时,很容易误认为和以前的data是同样的用法,只不过类型
变了而已,其实不然,这里的data是之前版本的pending和wq_data的复合体,起到了以前的pending和wq_data的作用。
func的参数是一个work_struct指针,指向的数据就是定义func的work_struct。看到这里,会有两个疑问,第一,如何把用户的数
据作为参数传递给func呢?以前有void
*data来作为参数,现在好像完全没有办法做到;第二,如何实现延迟工作?目前版本的work_struct并没有定义timer。
解决第一个问题,需要换一种思路。2.6.20版本之后使用工作队列需要把work_struct定义在用户的数据结构中,然后通过
container_of来得到用户数据。具体用法可以参考稍后的实作。
对于第二个问题,新的工作队列把timer拿掉的用意是使得work_struct更加单纯。首先回忆一下之前版本,只有在需要延迟执行工作时才会用到
timer,普通情况下timer是没有意义的,所以之前的做法在一定程度上有些浪费资源。所以新版本中,将timer从work_struct中拿掉,
然后又定义了一个新的结构delayed_work用于处理延迟执行:
struct delayed_work {
struct
work_struct work;
struct timer_list timer;
};
下面把API罗列一下,每
个函数的解释可参考之前版本的介绍或者之后的实作:
1) INIT_WORK(struct work_struct *work,
work_func_t func)
2) INIT_DELAYED_WORK(struct delayed_work *work,
work_func_t func)
3) int schedule_work(struct work_struct *work)
4)
int schedule_delayed_work(struct delayed_work *work, unsigned long
delay)
5) struct workqueue_struct *create_workqueue(const char *name)
6)
int queue_work(struct workqueue_struct *wq, struct work_struct *work)
7)
int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work
*work, unsigned long delay)
8) void flush_scheduled_work(void)
9)
void flush_workqueue(struct workqueue_struct *wq)
10) int
cancel_delayed_work(struct delayed_work *work)
11) void
destroy_workqueue(struct workqueue_struct *wq)
其中,1), 2), 4)
,7)和以前略有区别,其他用法完全一样。
实作:
struct my_struct_t {
char *name;
struct work_struct my_work;
};
void my_func(struct work_struct
*work)
{
struct my_struct_t *my_name = container_of(work,
struct my_struct_t, my_work);
printk(KERN_INFO “Hello world, my
name is %s!\n”, my_name->name);
}
struct workqueue_struct
*my_wq = create_workqueue(“my wq”);
struct my_struct_t my_name;
my_name.name
= “Jack”;
INIT_WORK(&(my_name.my_work), my_func);
queue_work(my_wq,
&my_work);
destroy_workqueue(my_wq);
http://blog.csdn.net/myvilla/archive/2007/07/21/1701587.aspx
http://blog.csdn.net/wangflash/archive/2009/08/14/4443868.aspx