linux中断管理—workqueue工作队列
文章目录
(注)本文所有代码均出自linux版本:
4.1.15
一、workqueue工作队列简介
工作队列是除软中断和tasklet
以外最常用的一种下半部机制
,其基本原理是:把work(需要推迟执行的函数)交由一个内核线程来执行,工作队列总是在进程上下文执行。
工作队列的优点:
- 因工作队列在进程下文中执行,因此工作队列允许重新调度和睡眠,是异步执行的进程上下文。
- 解决了如果软中断和tasklet执行时间过长会导致系统实时性下降等问题。
(1-1)work_struct工作
linux内核中使用work_struct
结构体来表示一个工作,如下定义(/inlcude/linux/workqueue.h):
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-2)workqueue工作队列
把工作(包括该工作任务的执行回调函数)添加到一个队列,称为workqueue
,即工作队列,然后通过worker-pool
中的内核工作线程(worker)去执行这个回调函数。工作队列使用workqueue_struct
结构体来表示,定义如下(/kernel/workqueue.c):
struct workqueue_struct {
struct list_head pwqs; /* WR: all pwqs of this wq */
struct list_head list; /* PR: list of all workqueues */
struct mutex mutex; /* protects this wq */
int work_color; /* WQ: current work color */
int flush_color; /* WQ: current flush color */
atomic_t nr_pwqs_to_flush; /* flush in progress */
struct wq_flusher *first_flusher; /* WQ: first flusher */
struct list_head flusher_queue; /* WQ: flush waiters */
struct list_head flusher_overflow; /* WQ: flush overflow list */
struct list_head maydays; /* MD: pwqs requesting rescue */
struct worker *rescuer; /* I: rescue worker */
int nr_drainers; /* WQ: drain in progress */
int saved_max_active; /* WQ: saved pwq max_active */
struct workqueue_attrs *unbound_attrs; /* WQ: only for unbound wqs */
struct pool_workqueue *dfl_pwq; /* WQ: only for unbound wqs */
#ifdef CONFIG_SYSFS
struct wq_device *wq_dev; /* I: for sysfs interface */
#endif
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
char name[WQ_NAME_LEN]; /* I: workqueue name */
/*
* Destruction of workqueue_struct is sched-RCU protected to allow
* walking the workqueues list without grabbing wq_pool_mutex.
* This is used to dump all workqueues from sysrq.
*/
struct rcu_head rcu;
/* hot fields used during command issue, aligned to cacheline */
unsigned int flags ____cacheline_aligned; /* WQ: WQ_* flags */
struct pool_workqueue __percpu *cpu_pwqs; /* I: per-cpu pwqs */
struct pool_workqueue __rcu *numa_pwq_tbl[]; /* FR: unbound pwqs indexed by node */
};
(1-3)worker工作者线程
linux 内核使用工作者线程(worker thread)来处理工作队列中的各个工作,linux 内核使用
worker 结构体表示工作者线程,worker 结构体定义如下(/kernel/workqueue_internal.h):
struct worker {
/* on idle list while idle, on busy hash table while busy */
union {
struct list_head entry; /* L: while idle */
struct hlist_node hentry; /* L: while busy */
};
struct work_struct *current_work; /*当前正在处理的work */
work_func_t current_func; /* 当前正在执行的work回调函数 */
struct pool_workqueue *current_pwq; /* 当前work所属的pool_workqueue*/
bool desc_valid; /* ->desc is valid */
struct list_head scheduled; /* 所有被调度并正准备执行的work都将加入到该链表中*/
/* 64 bytes boundary on 64bit, 32 on 32bit */
struct task_struct *task; /* 该工作线程的task_struct */
struct worker_pool *pool; /* 该工作线程所属的worker_pool */
/* L: for rescuers */
struct list_head node; /* worker挂入的 链表 pool->workers */
/* A: runs through worker->node */
unsigned long last_active; /* L: last active timestamp */
unsigned int flags; /* X: flags */
int id; /* 工作线程的id */
/*
* Opaque string set with work_set_desc(). Printed out with task
* dump for debugging - WARN, BUG, panic or sysrq.
*/
char desc[WORKER_DESC_LEN];
/* used only by rescuers to point to the target workqueue */
struct workqueue_struct *rescue_wq; /* I: the workqueue to rescue */
};
二、workqueue工作队列的使用
每一个worker
工作线程中都有一个工作队列,工作线程处理自己工作队列中的所有工作。
在实际开发中,推荐使用默认的workqueue·工作队列,而不是新创建workqueue。使用方法如下:
直接定义一个work_struct
结构体变量,然后使用INIT_WORK
宏来完成初始化工作,INIT_WORK定义如下:
#define INIT_WORK(_work, _func)
_work
表示要初始化的工作,_func
是工作对应的处理函数。
也可以使用 DECLARE_WORK 宏一次性完成工作的创建和初始化,宏定义如下:
#define DECLARE_WORK(n, f)
n 表示定义的工作(work_struct),f 表示工作对应的处理函数。和 tasklet 一样,工作也是需要调度才能运行的,工作的调度函数为schedule_work()
,函数原型如下所示:
bool schedule_work(struct work_struct *work)
使用cancel_work_sync()
取消一个工作,函数原型如下所示:
bool cancel_work_sync(struct work_struct *work)
当然也可以自己创建一个workqueue,特别是网络子系统、块设备子系统情况下等。具体步骤如下:
- 使用
alloc_workqueue()
创建新的workqueue。 - 使用
INIT_WORK()
宏声明一个work和该work的回调函数。 - 使用
queue_work()
在新的workqueue上调度一个work。 - 使用
flush_workqueue()
去flush 工作队列上的所有work。
除此之外,linux内核还提供了一个workqueue机制与timer机制相结合的延时机制—delayed_work
。
三、代码示例
//定义一个工作(work)
static struct work_sturct my_work;
//定义一个工作处理函数
void my_work_func(struct work_struct *work)
{
/*.......*/
}
//定义中断处理函数
irqreturn_t key_handler(int irq,void *dev_id)
{
//.........
//调度work
shcedule_work(&my_work);
// ......
}
/* 驱动入口函数
*/
static int __init my_demo_init(void)
{
//...
//初始化work
INIT_WORK(&my_work,my_work_func);
//注册中断处理 函数
request_irq(xxx_irq,key_handler,0,"xxxx",&xxx_dev);
//....
}
static void __exit my_demo_exit(void)
{
//执行一些释放操作
//....
}
module_init(my_demo_init);
module_exit(my_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("iriczhao");