一:工作队列概念
工作队列可以把工作推后,交由一个内核线程去执行,工作队列运行在进程上下文中,工作队列运行重新调度甚至睡眠内核驱动程序一般将下半部分交给内核缺省的工作者线程去做
二:驱动中使用工作队列的步骤
1,声明一个work_struct结构体
[cpp] view plaincopy
- struct work_struct work;
2,动态创建一个由work指向的工作,处理函数为func
[cpp] view plaincopy
- INIT_WORK(struct work_struct *work,void(*func)(void*),void*data)
- 例:INIT_WORK(&work,android_work);
- static void android_work(struct work_struct *data)
注:android_work()函数由一个内核线程调用,运行在进程上下文中,但是不能运行访问用户空间
3,对工作调度
工作调度就是将工作处理函数交给缺省的events工作线程
[cpp] view plaincopy
- schedule_work(&work);
三:工作队列的原理
与工作队列相关的数据结构是 struct workqueue_struct,系统默认的工作队列名称是:keventd_wq,默认的工作者线程叫:events/n,这里的n是处理器的编号,每个处理器对应一个线程。比如,单处理器的系统只有events/0这样一个线程。而双处理器的系统就会多一个events/1线程。默认工作队列keventd_wq和工作者线程events/n的建立在文件Kernel/kernel/workqueue.c中实现
四:创建自己的工作队列
[cpp] view plaincopy
- struct workqueue_struct *create_workqueue(cost char* name)
用于创建一个workqueue队列,为系统中的每个CPU都创建一个内核线程。输入参数
[cpp] view plaincopy
- create_singlethread_workqueue()只创建一个工作队列
- int queue_work(struct workqueue_struct*wq,struct work_struct *work)
五:补充
书上写的工作队列的实现是创建一个单独的线程来执行相应的work. 但是最新的内核实现却不是这样的,原先的工作队列的接口都快要废弃了,
New API:
[cpp] view plaincopy
- alloc_workqueue(name, flags, max_active)
- alloc_ordered_workqueue(const char *name, unsigned int flags)
- {
- return alloc_workqueue(name, WQ_UNBOUND | flags, 1);
- }
当向一个工作队列提交一个工作时,它并不是在指定的线程里运行,系统会维护一个Worker Pool, 每个Worker跑在一个单独的线程里,每一个CPU都有一个Worker Pool. 当有work需要处理时,就唤醒一个Worker,这样就减少了系统资源的占用(原先的实现是每创建一个工作队列,系统就创建一个线程,由于每个线程都需要有task_struct, pid等资源, 这样当系统中工作队列一多的话,资源占用率就很高了). 由于内核空间是所有进程共享的一块地址空间,因此在不同进程向工作队列提交的工作时,用户其实不用关心我这个工作到底是在哪个进程中处理的,但是这样的话,如果两个工作需要同步的话(比如访问一个共享的资源时),就得仔细考虑了,两个工作向同一个工作队列提交时,可能会被同时执行(分别跑在不同的CPU上), 这样RACE就产生了, 为了解决这个问题, 引入了WQ_UNBOUND标志 和 max_acitve = 1, 这两个参数指明了向这个工作队列提交一个工作时,这个工作不会绑定在特定的CPU上(如果没有指明WQ_UNBOUND 标志的话,在哪个CPU上提交的工作一定会在那个CPU上执行), max_active指明了这个工作队列后台有多少个worker线程与之绑定,默认参数为0,让系统来指定后台线程数。