Linux设备驱动程序 之 工作队列
工作队列可以把工作推后,交给一个内核线程去执行–这个下半部分总是会在进程上下文中执行;通过工作队列执行的代码占尽进程上下文的优势;最重要的是工作队列允许重新调度甚至睡眠;
在工作队列和软中断/tasklet中做出选择很容易;如果推后执行的任务需要睡眠,那么就选择工作队列;如果推后执行的任务不需要睡眠,那么就选择软中断或者tasklet;
如果需要用一个可以重新调度的实体来执行下半部处理,那么应该使用工作队列;它是唯一能在进程上下文中运行的下半部机制,也只有它才可以睡眠;这意味着如果需要获取大量的内存,或者在需要获取信号量时,或者需要执行阻塞式IO操作时,它会非常有用;如果不需要一个内核线程来推后执行工作,那么考虑使用tasklet;
创建推后的工作
可以通过DECLARE_WORK在编译时静态的创建work_struct结构实例并初始化:
#define DECLARE_WORK(n, f) \
struct work_struct n = __WORK_INITIALIZER(n, f) #define DECLARE_DELAYED_WORK(n, f) \
struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, ) #define DECLARE_DEFERRABLE_WORK(n, f) \
struct delayed_work n = __DELAYED_WORK_INITIALIZER(n, f, TIMER_DEFERRABLE)
或者在运行时动态的创建一个work_struct实例,然后使用下面函数进行初始化:
#define INIT_WORK(_work, _func) \
__INIT_WORK((_work), (_func), ) #define INIT_WORK_ONSTACK(_work, _func) \
__INIT_WORK((_work), (_func), ) #define INIT_DELAYED_WORK(_work, _func) \
__INIT_DELAYED_WORK(_work, _func, ) #define INIT_DELAYED_WORK_ONSTACK(_work, _func) \
__INIT_DELAYED_WORK_ONSTACK(_work, _func, )
工作队列处理函数
工作队列处理函数的原型是:
typedef void (*work_func_t)(struct work_struct *work);
这个函数会由一个工作者线程执行,因此,函数会运行在进程上下文中;默认情况下,允许响应中断,并不持有任何锁;如果需要,函数可以睡眠;
需要注意的是,尽管操作处理函数运行在进程上下文,但它不能访问用户空间,因为内核线程在用户空间没有相关的内存映射;通常在发送系统调用时,内核会代表用户空间进程运行,也只有此时它才会映射用户空间的内存;
调度工作(缺省工作队列)
如果要把给定工作的处理交给缺省的工作线程,则需要调用:
bool schedule_work(struct work_struct *work)
work马上会被调度,一旦其所在的处理器上的工作线程被唤醒,它就会被执行;
如果不希望马上被执行,而是希望它经过一段时间的延迟后才执行,则需要下面函数,可以在指定时间之后执行:
bool schedule_delayed_work(struct delayed_work *dwork,unsigned long delay)
这时,dwork会在delay指定的始终节拍用完之后才会执行;
刷新操作
排入队列的工作会在工作者线程下一次被唤醒时执行;有时,在继续下一步工作之前,必须保证一些操作已经执行完毕了;这一点对模块来说就很重要,在卸载之前,它就有可能需要调用下面的函数;而在内核的其他部分,为了防止竞争条件的出现,也可能需要确保不再有待处理的工作;
为了,内核准备了用于刷新指定工作队列的函数:
bool flush_work(struct work_struct *work); bool flush_delayed_work(struct delayed_work *dwork);
函数会一直等待,直到队列中的所有对象都被执行以后才返回;在等待所有待处理的工作执行的时候,该函数会进入休眠状态,所以只能在进程上下文中使用;
内核也提供了取消执行工作的函数:
bool cancel_work(struct work_struct *work);
bool cancel_work_sync(struct work_struct *work); bool cancel_delayed_work(struct delayed_work *dwork);
bool cancel_delayed_work_sync(struct delayed_work *dwork);
创建新的工作队列
如果缺省队列不能满足需求,则应该创建一个新的工作队列和与之对应的工作者线程;由于这么做会在每个处理器上都创建一个工作者线程,所以只有在你明确了必须要靠自己一套线程来提高性能的情况下,再创建自己的队列;
创建一个新的任务队列和与之相关的工作者线程,需要使用下面函数,name参数用于该内核线程的命名;
#define create_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, , (name))
#define create_freezable_workqueue(name) \
alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
WQ_MEM_RECLAIM, , (name))
#define create_singlethread_workqueue(name) \
alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)
这样就会在每个处理器上创建工作者线程,并准备好开始处理工作之前的准备工作;
创建一个工作的时候无须考虑工作队列的类型,在创建之后,可以调用下面的函数;这些函数与schedule_work()相似,唯一的区别是它们针对给定的工作队列而不是缺省的队列进行操作;
bool queue_work(struct workqueue_struct *wq,
struct work_struct *work) bool queue_delayed_work(struct workqueue_struct *wq,
struct delayed_work *dwork,
unsigned long delay)
刷新指定的工作队列可以使用下面函数:
void flush_workqueue(struct workqueue_struct *wq);
结束对工作队列的使用后,可以使用下面函数释放资源:
void destroy_workqueue(struct workqueue_struct *wq);
Linux设备驱动程序 之 工作队列的更多相关文章
- linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)
原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...
- 【转】linux设备驱动程序中的阻塞机制
原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...
- Linux设备驱动程序 第三版 读书笔记(一)
Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...
- Linux设备驱动程序学习之分配内存
内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...
- 教你写Linux设备驱动程序:一个简短的教程
教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html
- linux设备驱动程序_hello word 模块编译各种问题集锦
在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...
- Linux设备驱动程序学习----1.设备驱动程序简介
设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介 Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...
- Linux设备驱动程序学习----2.内核模块与应用程序的对比
内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...
- Linux设备驱动程序学习----3.模块的编译和装载
模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...
随机推荐
- 使用shared memory 计算矩阵乘法 (其实并没有加速多少)
#include "cuda_runtime.h" #include "device_launch_parameters.h" #include "d ...
- K2 BPM_K2受邀出席QAD 2019年亚太区用户大会_全业务流程管理专家
6月12-13日,K2受邀参加了以“云聚创新,智造未来”为主题的QAD 2019年亚太区用户大会.会议上K2同与会嘉宾们共商制造业数字化转型,就如何用流程赋能企业实现智能制造进行了精彩分享. 近期发布 ...
- ASE19团队项目 beta阶段 model组 scrum1 记录
本次会议于12月2日,18时30分在微软北京西二号楼sky garden召开,持续25分钟. 与会人员:Jiyan He, Kun Yan, Lei Chai, Linfeng Qi, Xueqing ...
- 嵌套的页面——自适应高度与跨越操作DOM
<div id="myIframeId"> <iframe ref="myIframe" name="odpIframeName&q ...
- 深入理解Kubernetes资源限制:CPU
写在前面 在上一篇关于Kubernetes资源限制的文章我们讨论了如何通过ResourceRequirements设置Pod中容器内存限制,以及容器运行时是如何利用Linux Cgroups实现这些限 ...
- 14、RALM: 实时 look-alike 算法在推荐系统中的应用
转载:https://zhuanlan.zhihu.com/p/71951411 RALM: 实时 look-alike 算法在推荐系统中的应用 0. 导语 本论文题为<Real-time At ...
- java-利用BitSet查找素数
高效存储为序列可以使用位积,由于位集将位包装在字节里,所以位集要比使用Boolean对象的ArrayList更高效. 自己的代码,素数是false public class Sieve { @Test ...
- MySQL内存结构
实际上MySQL内存的组成和Oracle类似,也可以分为SGA(系统全局区)和PGA(程序缓存区). mysql>show variables like "%buffer%" ...
- MySQL之text字段
TEXT类型一般分为 TINYTEXT(255长度).TEXT(65535). MEDIUMTEXT(int最大值16M),和LONGTEXT(long最大值4G)这四种,它被用来存储非二进制字符集, ...
- c++对c的扩展----什么时候分配内存
const分配内存的时机,编译器编译的时候分配内存 const相当于宏,用来取代c语言的#define #include<iostream> using namespace std; vo ...