Linux中的工作队列

本文详细介绍了Linux工作队列机制,包括其在2.6.20版本前后的变化,以及提供实作案例帮助理解。重点阐述了工作队列与BH、Tasklets的区别,以及如何使用API进行初始化、调度、延迟执行等工作。
摘要由CSDN通过智能技术生成

[前记]Linux自从2.6.20之后,工作队列发生了一些变化,目前从网络上搜索的资料一般都是介绍老版本的工作队列,很少见到对新版本的介绍。本文对新老版本都做了简要概述,并分别提供了简单的实作案例。


*******************************************************************************************************************

工作队列 (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_name.my_work));


destroy_workqueue(my_wq);


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值