这样,工作队列同意又一次调度甚至是睡眠。
它创建的这些内核线程称作工作者线程。
工作队列能够让你的驱动程序创建一个专门的工作者线程来处理须要推后的工作。只是,工作队列子系统提供了一个缺省的工作者线程来处理这些工作。因此,工作队列最主要的表现形式就转变成一个把须要推后运行的任务交给特定的通用线程这样一种接口。缺省的工作线程叫做event/n.每一个处理器相应一个线程,这里的n代表了处理器编号。除非一个驱动程序或者子系统必须建立一个属于自己的内核线程。否则不妨使用缺省线程。
# ps x | grep event | grep -v grep 9 ? S 0:00 [events/0] 10 ? S 0:00 [events/1]
struct workqueue_struct { struct cpu_workqueue_struct *cpu_wq; //该数组每一项相应系统中的一个处理器 struct list_head list; const char *name; int singlethread; int freezeable; /* Freeze threads during suspend */ int rt;#ifdef CONFIG_LOCKDEP struct lockdep_map lockdep_map;#endif}
struct cpu_workqueue_struct { spinlock_t lock; //保护该结构 struct list_head worklist; //工作列表 wait_queue_head_t more_work; //等待队列。当中的工作者线程因等待而处于睡眠状态 struct work_struct *current_work; struct workqueue_struct *wq; //关联工作队列结构 struct task_struct *thread; // 关联线程,指向结构中工作者线程的进程描写叙述符指针} ____cacheline_aligned;
在它初始化完以后,这个函数运行一个死循环运行一个循环并開始休眠,当有操作被插入到队列的时候,线程就会被唤醒。以便运行这些操作。
当没有剩余的时候,它又会继续休眠。工作由work_struct
(位于<kernel/workqueue.c>中) 结构表示:struct work_struct { atomic_long_t data;...... struct list_head entry;//连接全部链表 work_func_t func;.....};
工作一旦运行完成,它就将对应的work_struct对象从链表上移去,当链表不再有对象时,它就继续休眠。woker_thread()函数例如以下:
static int worker_thread(void *__cwq){ struct cpu_workqueue_struct *cwq = __cwq; DEFINE_WAIT(wait); if (cwq->wq->freezeable) set_freezable(); for (;;) { //线程将自己设置为休眠状态并把自己增加等待队列 prepare_to_wait(&cwq->more_work, &wait, TASK_INTERRUPTIBLE); if (!freezing(current) && !kthread_should_stop() && list_empty(&cwq->worklist)) schedule();//假设工作对列是空的,线程调用schedule()函数进入睡眠状态 finish_wait(&cwq->more_work, &wait); try_to_freeze();//假设链表有对象,线程就将自己设为执行态,脱离等待队列 if (kthread_should_stop()) break;//再次调用run_workqueue()执行推后的工作 run_workqueue(cwq); } return 0;}
static void run_workqueue(struct cpu_workqueue_struct *cwq) { spin_lock_irq(&cwq->lock); while (!list_empty(&cwq->worklist)) { //链表不为空时,选取下一个节点对象 struct work_struct *work = list_entry(cwq->worklist.next, struct work_struct, entry); //获取希望运行的函数func及其參数data work_func_t f = work->func;...... trace_workqueue_execution(cwq->thread, work); cwq->current_work = work; //把该结点从链表上解下来 list_del_init(cwq->worklist.next); spin_unlock_irq(&cwq->lock); BUG_ON(get_wq_data(work) != cwq); //将待处理标志位pending清0 work_clear_pending(work); lock_map_acquire(&cwq->wq->lockdep_map); lock_map_acquire(&lockdep_map); //运行函数 f(work); lock_map_release(&lockdep_map); lock_map_release(&cwq->wq->lockdep_map);...... spin_lock_irq(&cwq->lock); cwq->current_work = NULL; } spin_unlock_irq(&cwq->lock);}
能够使用缺省的events任务队列,也能够创建新的工作者线程。
假设须要,函数能够睡眠。
须要注意的是,虽然处理函数执行在进程上下文中,但它不能訪问用户空间,由于内核线程在用户空间没有对应的内存映射。函数原型例如以下:
当然假设不想高速运行,而是想延迟一段时间运行。调用
int schedule_work(struct work_struct *work){ return queue_work(keventd_wq, work);}int schedule_delayed_work(struct delayed_work *dwork, unsigned long delay) { return queue_delayed_work(keventd_wq, dwork, delay);}
因为这些原因。内核提供了一个用于刷新指定工作队列的函数:
取消延迟运行的工作应该调用:int cancel_delayed_work(struct work_struct *work);这个函数能够取消不论什么与work_struct 相关挂起的工作。
#include上面的操作是使用缺省的工作队列,以下来看一下创建一个新的工作队列是怎样操作的?#include #include //work_strcut//struct work_struct ws;struct delayed_work dw;void workqueue_func(struct work_struct *ws) //处理函数{ printk(KERN_ALERT"Hello, this is shallnet!\n");}static int __init kwq_init(void){ printk(KERN_ALERT"===%s===\n", __func__); //INIT_WORK(&ws, workqueue_func); //建须要推后完毕的工作 //schedule_work(&ws); //调度工作 INIT_DELAYED_WORK(&dw, workqueue_func); schedule_delayed_work(&dw, 10000); return 0;}static void __exit kwq_exit(void){ printk(KERN_ALERT"===%s===\n", __func__); flush_scheduled_work();}module_init(kwq_init);module_exit(kwq_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("shallnet");MODULE_DESCRIPTION("blog.csdn.net/shallnet");
struct workqueue_struct *create_workqueue(const char *name);name是新内核线程的名字。比方缺省events队列的创建是这样使用的:
struct workqueue_struct *keventd_wq;kevent_wq = create_workqueue("event");
然后调用例如以下函数进行调度:
int queue_work(struct workqueue_struct *wq, struct work_struct *work);int queue_delayed_work(struct workqueue_struct *wq,struct delayed_work *work,unsigned long delay);
#include#include #include //work_strcutstruct workqueue_struct *sln_wq = NULL;//struct work_struct ws;struct delayed_work dw;void workqueue_func(struct work_struct *ws){ printk(KERN_ALERT"Hello, this is shallnet!\n");}static int __init kwq_init(void){ printk(KERN_ALERT"===%s===\n", __func__); sln_wq = create_workqueue("sln_wq"); //创建名为sln_wq的工作队列 //INIT_WORK(&ws, workqueue_func); //queue_work(sln_wq, &ws); INIT_DELAYED_WORK(&dw, workqueue_func); // queue_delayed_work(sln_wq, &dw, 10000); // return 0;}static void __exit kwq_exit(void){ printk(KERN_ALERT"===%s===\n", __func__); flush_workqueue(sln_wq);}module_init(kwq_init);module_exit(kwq_exit);MODULE_LICENSE("GPL");MODULE_AUTHOR("shallnet");MODULE_DESCRIPTION("blog.csdn.net/shallnet");
当中tasklet基于软中断,而工作队列靠内核线程实现。
假设你须要把任务推后到进程上下文中。或你须要休眠。那就仅仅有使用工作队列了。