Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2039433
  • 博文数量: 610
  • 博客积分: 11499
  • 博客等级: 上将
  • 技术积分: 5511
  • 用 户 组: 普通用户
  • 注册时间: 2008-03-12 19:27
文章分类

全部博文(610)

文章存档

2016年(5)

2015年(18)

2014年(12)

2013年(16)

2012年(297)

2011年(45)

2010年(37)

2009年(79)

2008年(101)

分类: LINUX

2011-06-21 23:20:43

(2011-02-28 13:49)

标签style  分类: linux 源码分析

工作队列(work queue)Linux kernel中将工作推后执行的一种机制。这种机制和BHTasklets不同之处在于工作队列是把推后的工作交由一个内核线程去执行,因此工作队列的优势就在于它允许重新调度甚至睡眠。

工作队列是2.6内核开始引入的机制,在2.6.20之后,工作队列的数据结构发生了一些变化,因此本文分成两个部分对2.6.20之前和之后的版本分别做介绍。

 

I2.6.0~2.6.19

 

数据结构:

view sourceprint?

1

struct work_struct {

2

    unsigned long pending;

 

3

    struct list_head entry;

4

    void (*func)(void *);

 

5

    void *data;

6

    void *wq_data;

 

7

    struct timer_list timer;

8

};

pending是用来记录工作是否已经挂在队列上;

entry是循环链表结构;

func作为函数指针,由用户实现;

data用来存储用户的私人数据,此数据即是func的参数;

wq_data一般用来指向工作者线程(工作者线程参考下文);

timer是推后执行的定时器。

work_struct的这些变量里,funcdata是用户使用的,其他是内部变量,我们可以不用太过关心。

 

API

view sourceprint?

1

INIT_WORK(_work, _func, _data);

2

int schedule_work(struct work_struct *work);

 

3

int schedule_delayed_work(struct work_struct *work, unsigned long delay);

4

void flush_scheduled_work(void);

 

5

int cancel_delayed_work(struct work_struct *work);

1初始化指定工作,目的是把用户指定的函数_func_func需要的参数_data赋给work_structfuncdata变量。

2对工作进行调度,即把给定工作的处理函数提交给缺省的工作队列和工作者线程。工作者线程本质上是一个普通的内核线程,在默认情况下,每个CPU均有一个类型为events”的工作者线程,当调用schedule_work时,这个工作者线程会被唤醒去执行工作链表上的所有工作。

3延迟执行工作,与schedule_work类似。

4刷新缺省工作队列。此函数会一直等待,直到队列中的所有工作都被执行。

5flush_scheduled_work并不取消任何延迟执行的工作,因此,如果要取消延迟工作,应该调用cancel_delayed_work

 

以上均是采用缺省工作者线程来实现工作队列,其优点是简单易用,缺点是如果缺省工作队列负载太重,执行效率会很低,这就需要我们创建自己的工作者线程和工作队列。

API

view sourceprint?

1

struct workqueue_struct *create_workqueue(const char *name);

2

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

 

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);

1创建新的工作队列和相应的工作者线程,name用于该内核线程的命名。

2类似于schedule_work,区别在于queue_work把给定工作提交给创建的工作队列wq而不是缺省队列。

3延迟执行工作。

4刷新指定工作队列。

5释放创建的工作队列。

 

下面一段代码可以看作一个简单的实作:

view sourceprint?

01

void my_func(void *data)

02

{

 

03

    char *name = (char *)data;

04

    printk(KERN_INFO “Hello world, my name is %s!\n”, name);

 

05

}

06

  

 

07

struct workqueue_struct *my_wq = create_workqueue(“my wq”);

08

struct work_struct my_work;

 

09

  

10

INIT_WORK(&my_work, my_func, “Jack”);

 

11

queue_work(my_wq, &my_work);

12

  

 

13

destroy_workqueue(my_wq);

 

 

II2.6.20~2.6.??

 

2.6.20起,工作队列的数据结构发生了一些变化,使用时不能沿用旧的方法。

 

数据结构:

view sourceprint?

1

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

2

  

 

3

struct work_struct {

4

    atomic_long_t data;

 

5

    struct list_head entry;

6

    work_func_t func;

 

7

};

2.6.19之前的版本相比,work_struct瘦身不少。粗粗一看,entry和之前的版本相同,funcdata发生了变化,另外并无其他的变量。

entry我们不去过问,这个和以前的版本完全相同。data的类型是atomic_long_t,这个类型从字面上看可以知道是一个原子类型。第一次看到这个变量时,很容易误认为和以前的data是同样的用法,只不过类型变了而已,其实不然,这里的data是之前版本的pendingwq_data的复合体,起到了以前的pendingwq_data的作用。

func的参数是一个work_struct指针,指向的数据就是定义funcwork_struct

看到这里,会有两个疑问,第一,如何把用户的数据作为参数传递给func呢?以前有void *data来作为参数,现在好像完全没有办法做到;第二,如何实现延迟工作?目前版本的work_struct并没有定义timer

 

解决第一个问题,需要换一种思路。2.6.20版本之后使用工作队列需要把work_struct定义在用户的数据结构中,然后通过container_of来得到用户数据。具体用法可以参考稍后的实作。

 

对于第二个问题,新的工作队列把timer拿掉的用意是使得work_struct更加单纯。首先回忆一下之前版本,只有在需要延迟执行工作时才会用到timer,普通情况下timer是没有意义的,所以之前的做法在一定程度上有些浪费资源。所以新版本中,将timerwork_struct中拿掉,然后又定义了一个新的结构delayed_work用于处理延迟执行:

view sourceprint?

1

struct delayed_work {

2

    struct work_struct work;

 

3

    struct timer_list timer;

4

};

 

下面把API罗列一下,每个函数的解释可参考之前版本的介绍或者之后的实作:

view sourceprint?

01

INIT_WORK(struct work_struct *work, work_func_t func);

02

INIT_DELAYED_WORK(struct delayed_work *work, work_func_t func);

 

03

int schedule_work(struct work_struct *work);

04

int schedule_delayed_work(struct delayed_work *work, unsigned long delay);

 

05

struct workqueue_struct *create_workqueue(const char *name);

06

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

 

07

int queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *work, unsigned long delay);

08

void flush_scheduled_work(void);

 

09

void flush_workqueue(struct workqueue_struct *wq);

10

int cancel_delayed_work(struct delayed_work *work);

 

11

void destroy_workqueue(struct workqueue_struct *wq);

其中,1247和以前略有区别,其他用法完全一样。

 

实作:

view sourceprint?

01

struct my_struct_t {

02

    char *name;

 

03

    struct work_struct my_work;

04

};

 

05

  

06

void my_func(struct work_struct *work)

 

07

{

08

    struct my_struct_t *my_name = container_of(work, struct my_struct_t, my_work);

 

09

    printk(KERN_INFO “Hello world, my name is %s!\n”, my_name->name);

10

}

 

11

  

12

struct workqueue_struct *my_wq = create_workqueue(“my wq”);

 

13

struct my_struct_t my_name;

14

  

 

15

my_name.name = “Jack”;

16

  

 

17

INIT_WORK(&(my_name.my_work), my_func);

18

queue_work(my_wq, &(my_name.my_work));

 

19

  

20

destroy_workqueue(my_wq);

版权声明: 原创作品,允许转载,转载时请务必以超链接形式标明文章原始出处 、作者信息和本声明。否则将追究法律责任。

 

 

阅读(496) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~