中断下半部的工作队列机制
文章目录
工作队列处理机制
1.三个重要结构体:
1.1 struct workqueue_struct:
struct workqueue_struct {
unsigned int flags; /* W: WQ_* flags */
union {
struct cpu_workqueue_struct __percpu *pcpu;
struct cpu_workqueue_struct *single;
unsigned long v;
} cpu_wq; /* I: cwq's */
struct list_head list; /* W: list of all workqueues */
struct mutex flush_mutex; /* protects wq flushing */
int work_color; /* F: current work color */
int flush_color; /* F: current flush color */
atomic_t nr_cwqs_to_flush; /* flush in progress */
struct wq_flusher *first_flusher; /* F: first flusher */
struct list_head flusher_queue; /* F: flush waiters */
struct list_head flusher_overflow; /* F: flush overflow list */
mayday_mask_t mayday_mask; /* cpus requesting rescue */
struct worker *rescuer; /* I: rescue worker */
int nr_drainers; /* W: drain in progress */
int saved_max_active; /* W: saved cwq max_active */
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
char name[]; /* I: workqueue name */
};
1.2. struct worker
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; /* L: work being processed */
struct cpu_workqueue_struct *current_cwq; /* L: current_work's cwq */
struct list_head scheduled; /* L: scheduled works */
struct task_struct *task; /* I: worker task */
struct global_cwq *gcwq; /* I: the associated gcwq */
/* 64 bytes boundary on 64bit, 32 on 32bit */
unsigned long last_active; /* L: last active timestamp */
unsigned int flags; /* X: flags */
int id; /* I: worker id */
struct work_struct rebind_work; /* L: rebind worker to cpu */
};
1.3. struct work_struct
struct work_struct {
atomic_long_t data; //传参数据
struct list_head entry; //链表结构,将任务挂载到queue的挂载点
work_func_t func; //函数指针
#ifdef CONFIG_LOCKDEP
struct lockdep_map lockdep_map;
#endif
};
2.为什么要有workqueue机制
Linux中的Workqueue机制就是为了简化内核线程的创建。通过调用workqueue的接口就能创建内核线程。并且可以根据当前系统CPU的个数创建线程的数量,使得线程处理的事务能够并行化。workqueue是内核中实现简单而有效的机制,显然简化了内核daemon的创建,方便了用户的编程
daemon与服务的关系:我们系统为了某些功能必须要提供一些服务,而如何产生这些服务?那就是需要一个程序来运行产生这个服务。那么这个程序相对于这个服务来说就是daemon。daemon与服务是两个不同的东西:一个服务需要daemon在后台中运行,没有这个daemon就不会有这个服务。用ps或是top命令查看进程时,会看到很多xxxd的进程。这就是一些daemon的进程
3. workqueue的实现原理
如下图:
步骤:
-
创建工作队列
-
创建工作
-
调度工作(把工作加入到链表中)
3.1创建工作队列
方法:
-
create_singlethread_workqueue(name)
这个函数完成了workqueue的初始化。上图中的cwq是一per-CPU类型的地址空间。对于create_singlethread_workqueue而言,即使是对于多CPU系统,内核也只负责创建一个worker_thread内核进程。该内核进程被创建之后,会先定义一个图中的wait节点,然后在一循环体中检查cwq中的worklist,如果该队列为空,那么就会把wait节点加入到cwq中的more_work中,然后休眠在该等待队列中。
调用这个函数之后,会创建一个内核线程去判断是否工作链表中有工作要做
2.create_workqueue
相对于create_singlethread_workqueue, create_workqueue同样会分配一个wq的工作队列,但是不同之处在于,对于多CPU系统而言,对每一个CPU,都会为之创建一个per-CPU的cwq结构,对应每一个cwq,都会生成一个新的worker_thread进程。但是当用queue_work向cwq上提交work节点时,是哪个CPU调用该函数,那么便向该CPU对应的cwq上的worklist上增加work节点。
3.2 创建工作
方法:
-
-
静态:DECLARE_WORK(name,void (*func) (void *), void *data);在编译时静态地创建work_struct
-
动态: INIT_WORK(structwork_struct *work, void(*func) (void *), void *data);
3.3调度工作
方法:
1. queue_work(struct workqueue_struct*,struct work_struct*)//把工作插入指定的工作队列 2. queue_delayed_work //延迟调度执行一个指定workqueue中的任务,功能与queue_work类似,输入参数多了一个delay。 3. schedule_work(struct work_struct*)//调度执行一个具体的任务,执行的任务将会被挂入Linux系统提供的workqueue:keventd_wq 4. schedule_delayed_work //延迟一定时间去执行一个具体的任务,功能与schedule_work类似
这些函数内部除了把工作结构体加到工作链表以外,还会唤醒内核线程来处理。
例如Driver调用queue_work(struct workqueue_struct *wq, struct work_struct *work)向wq中加入工作节点。work会依次加在cwq->worklist所指向的链表中。queue_work向cwq->worklist中加入一个work节点,同时会调用wake_up来唤醒休眠在cwq->more_work上的worker_thread进程。
当worker_thread再次被调度,开始处理cwq->worklist中的所有work节点…当所有work节点处理完毕,worker_thread重新将wait节点加入到cwq->more_work,然后再次休眠在该等待队列中直到Driver调用queue_work…
4.代码示例
#include <linux/module.h>
#include <linux/init.h>
#include <linux/workqueue.h>
static struct workqueue_struct *queue=NULL;
static struct work_struct work;
staticvoid work_handler(struct work_struct *data)
{
printk(KERN_ALERT"work handler function.\n");
}
static int __init test_init(void)
{
queue=create_singlethread_workqueue("hello world");/*创建一个单线程的工作队列*/
if (!queue)
goto err;
INIT_WORK(&work,work_handler); //动态创建工作,联系工作内容
schedule_work(&work);
return0;
err:
return-1;
}
static void __exit test_exit(void)
{
destroy_workqueue(queue);
}
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);
参考资料
https://www.cnblogs.com/sky-heaven/p/5847520.html