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的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...
随机推荐
- layer弹出层移动端组件
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta name ...
- SQL alchemy
SQL alchemy介绍 SQL alchemy是orm思想的一个具体实现的产品 orm:对象关系映射思想 Object Relational Mapping 就是将数据库里的资源与面向对象中的类和 ...
- 根据导入xlxs的文件,来写入数据库
今天讲解一下上传文件.前台必须保持传参类型"multipart/form-data" 后台可以设定 public static final String MULTIPART_FOR ...
- stm32 usart 串口
比特率是每秒钟传输二进制代码的位数,单位是:位/秒(bps).如每秒钟传送240个字符, 而每个字符格式包含10位(1个起始位.1个停止位.8个数据位),这时的比特率为: 10位 × 240个/秒 = ...
- perl判断文件是否存在 perl -e
perl 有很多的命令 其中有 -e 是判断文件和目录是否存在 代码如下: #!/usr/bin/perl $fileExist = -e "/var/log/messages&qu ...
- 【Zookeeper】集群环境搭建
一.概述 1.1 Zookeeper的角色 1.2 Zookeeper的读写机制 1.3 Zookeeper的保证 1.4 Zookeeper节点数据操作流程 二.Zookeeper 集群环境搭建 2 ...
- zabbix初级进阶
目录 一.理论概述 zabbix功用 运行条件 缺点 zabbix组件 部署 web安装zabbix 优化 总结 这篇文章主要对zabbix有一个全面且简单的了解 一.理论概述 zabbix功用 检测 ...
- Linux下zookeeper集群搭建
Linux下zookeeper集群搭建 部署前准备 下载zookeeper的安装包 http://zookeeper.apache.org/releases.html 我下载的版本是zookeeper ...
- 《JavaScript DOM编程艺术》(第二版)学习笔记(一)
这本书去年就买了但一直没看,闲暇的时候看了下,发现里面写的内容还真是不错,所以决定一边在博客上记录些学习的笔记,以便以后观看及查找方便. js文件最好的做法是放在< body>标签里,这样 ...
- Java字节码常量池深入剖析
继续来分析Java字节码,上一节分析了魔数的规则,接下来继续往下分析,其上次总结的规则也一起贴出来: 1.使用javap -verbose命令分析一个字节码文件时,将会分析该字节码文件的魔数.版本号. ...