workqueue 简介:
工作队列(workqueue)是 Linux 内核中一种将工作推迟到进程上下文执行的机制,因此工作队列允许被重新调度甚至是睡眠。它属于中断下半部机制的一种。但它并不一定要和中断一起使用,即使没有中断,也可以在Linux驱动中独立使用。
Linux中通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。
Linux驱动编程使用 workqueue 时,我们需要注意一下2点:
- 系统默认有system_wq作为workqueue供我们使用
- 我们也能创建自己的workqueue
一、workqueue的API函数
源码路径:kernel/workqueue.c
头文件路径:include/linux/workqueue.h
1. create_workqueue(name)
功能:用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。
输入参数:
name:workqueue的名称
2. create_singlethread_workqueue(name)
功能:用于创建workqueue,只创建一个内核线程。输入参数:
输入参数:
name:workqueue名称
3. destroy_workqueue(struct workqueue_struct *wq)
功能:释放workqueue队列。输入参数:
输入参数:
workqueue_struct:需要释放的workqueue队列指针
4. schedule_work(struct work_struct *work);
功能:调度执行一个具体的任务
输入参数:
work_struct:具体任务对象指针
5. schedule_delayed_work(struct delayed_work *dwork, unsigned long delay)
功能:延迟一定时间去执行一个具体的任务,功能与schedule_work类似,多了一个延迟时间,
输入参数:
work_struct:具体任务对象指针
delay:延迟时间
6. queue_work(struct workqueue_struct *wq, struct work_struct *work)
功能:调度执行一个指定workqueue中的任务。
输入参数:
workqueue_struct:指定的workqueue指针
work_struct:具体任务对象指针
7. queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork, unsigned long delay)
功能:延迟调度执行一个指定workqueue中的任务。功能与queue_work类似,输入参数多了一个delay。
create_workqueue() 最终调用 alloc_workqueue()函数
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
二、使用方法
1、默认的workqueue队列
- 工作队列 work_struct
//结构体
struct work_struct {
atomic_long_t data; //相关标志位
struct list_head entry; //链表节点
work_func_t func; //工作函数
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map; //死锁检测
#endif
};
1、定义
static struct work_struct work;
static void work_callback_func(struct work_struct *work)
{
}
2、初始化
INIT_WORK(&work, work_callback_func);
3、触发工作队列
schedule_work(&work);
- 延时工作队列 delayed_work
//延时结构体
struct delayed_work {
struct work_struct work;
struct timer_list timer;
/* target workqueue and CPU ->timer uses to queue ->work */
struct workqueue_struct *wq;
int cpu;
};
1、定义
static struct delayed_work delayed_work;
static void delayed_callback_func(struct work_struct *work)
{
}
2、初始化
INIT_DELAYED_WORK(&delayed_work, delayed_callback_func);
3、触发延时工作队列
schedule_delayed_work(&delayed_work, 5 * HZ);
三、示例
1、系統默认的workqueue队列
linux系统已经提供了一个默认的workqueue队列“system_wq”,并提供了一套函数来方便大家使用
#include <linux/module.h>
#include <linux/sched.h>
#include <linux/kthread.h>
#include <linux/err.h>
#include <linux/delay.h>
static struct work_struct test_work; //工作队列
static struct delayed_work test_delayed_work; //延时工作队列
static void test_func(struct work_struct *work)
{
printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
}
static void test_delayed_func(struct work_struct *work)
{
printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
}
static int __init test_init(void)
{
int ret;
printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
INIT_WORK(&test_work, test_func); //初始化
ret = schedule_work(&test_work); //调度工作队列
INIT_DELAYED_WORK(&test_delayed_work, test_delayed_func); //初始化
ret = schedule_delayed_work(&test_delayed_work, 5 * HZ); //调度延时工作队列
printk("%s, %d, %ld\n", __func__, __LINE__, jiffies);
return 0;
}
static void __exit test_exit(void)
{
cancel_work_sync(&test_work);
cancel_delayed_work_sync(&test_delayed_work);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
当然,你也可以用DECLARE_WORK来完成和INIT_WORK同样初始化work的工作。区别是DECLARE_WORK是预编译命令,而INIT_WORK可以在code中动态初始化。
schedule_work 用的就是 system_wq。我们看看schedule_work函数定义,一切就真相大白了!
static inline bool schedule_work(struct work_struct *work)
{
return queue_work(system_wq, work);
}
2、自己创建workqueue队列
#include <linux/module.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/list.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/workqueue.h>
#include <linux/delay.h>
//工作以队列结构组织成工作队列(workqueue),其数据结构为workqueue_struct,
static struct workqueue_struct *test_wq = NULL;
//把推后执行的任务叫做工作(work),描述它的数据结构为work_struct
static struct work_struct work;
//定义工作队列调用函数
void work_func(struct work_struct *work){
printk(KERN_INFO "---%s---\n",__func__);
}
static int __init test_init(void){
int i;
/* 创建工作队列workqueue_struct,该函数会为cpu创建内核线程 */
test_wq = create_workqueue("test_wq");
/* 初始化工作work_struct,指定工作函数 */
INIT_WORK(&work,work_func);
/* 将工作加入到工作队列中,最终唤醒内核线程 */
queue_work(test_wq, &work);
for(i = 0; i < 3; i++)
{
printk(KERN_INFO "%s, %s, %d\n", __FILE__, __func__, __LINE__);
}
return 0;
}
static void __exit test_exit(void){
printk(KERN_INFO "%s, %s, %d\n", __FILE__, __func__, __LINE__);
}
运行结果可以看出,是先执行的test_init里的printk()打印程序,后执行workqueue的程序
test/test.c, test_init, 35
test/test.c, test_init, 35
test/test.c, test_init, 35
---work_func---
test/test.c, test_exit, 41
新建workqueue可以避免system_wq中被挂的work过多,或是某个被挂上去的work处理函数死锁,导致我们的work没有及时调度到。
但是,创建太多workqueue会导致系统调度开销的变大,所以需要取舍。

2919

被折叠的 条评论
为什么被折叠?



