工作队列可以把工作推后,交给一个内核线程去执行–这个下半部分总是会在进程上下文中执行;通过工作队列执行的代码占尽进程上下文的优势;最重要的是工作队列允许重新调度甚至睡眠;

在工作队列和软中断/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设备驱动程序 之 工作队列的更多相关文章

  1. linux设备驱动程序该添加哪些头文件以及驱动常用头文件介绍(转)

    原文链接:http://blog.chinaunix.net/uid-22609852-id-3506475.html 驱动常用头文件介绍 #include <linux/***.h> 是 ...

  2. 【转】linux设备驱动程序中的阻塞机制

    原文网址:http://www.cnblogs.com/geneil/archive/2011/12/04/2275272.html 阻塞与非阻塞是设备访问的两种方式.在写阻塞与非阻塞的驱动程序时,经 ...

  3. Linux设备驱动程序 第三版 读书笔记(一)

    Linux设备驱动程序 第三版 读书笔记(一) Bob Zhang 2017.08.25 编写基本的Hello World模块 #include <linux/init.h> #inclu ...

  4. Linux设备驱动程序学习之分配内存

    内核为设备驱动提供了一个统一的内存管理接口,所以模块无需涉及分段和分页等问题. 我已经在第一个scull模块中使用了 kmalloc 和 kfree 来分配和释放内存空间. kmalloc 函数内幕 ...

  5. 教你写Linux设备驱动程序:一个简短的教程

    教你写Linux设备驱动程序:一个简短的教程 http://blog.chinaunix.net/uid-20799298-id-99675.html

  6. linux设备驱动程序_hello word 模块编译各种问题集锦

    在看楼经典书籍<linux设备驱动程序>后,第一个程序就是编写一个hello word 模块. 原以为非常easy,真正弄起来,发现问题不少啊.前两天编过一次,因为没有记录,今天看的时候又 ...

  7. Linux设备驱动程序学习----1.设备驱动程序简介

    设备驱动程序简介 更多内容请参考Linux设备驱动程序学习----目录 1. 简介   Linux系统的优点是,系统内部实现细节对所有人都是公开的.Linux内核由大量复杂的代码组成,设备驱动程序可以 ...

  8. Linux设备驱动程序学习----2.内核模块与应用程序的对比

    内核模块与应用程序的对比 更多内容请参考Linux设备驱动程序学习----目录 1. 内核模块与应用程序的对比 内核模块和应用程序之间的不同之处: 大多数中小规模的应用程序是从头到尾执行单个任务,而模 ...

  9. Linux设备驱动程序学习----3.模块的编译和装载

    模块的编译和装载 更多内容请参考Linux设备驱动程序学习----目录 1. 设置测试系统 第1步,要先从kernel.org的镜像网站上获取一个主线内核,并安装到自己的系统中,因为学习驱动程序的编写 ...

随机推荐

  1. java毫秒级别定时器

    java每100毫秒执行一次 //每100毫秒秒执行一次 @Scheduled(fixedRate = 100) public void testScheduler() { System.out.pr ...

  2. css垂直居中如何实现

    利用CSS3的transform:translate .center{ width:%; position: absolute; top: %; left: %; -moz-transform: tr ...

  3. IDEA 使用与总结

    一.IDEA和常用软件下载1.IDEA激活码网站:http://idea.lanyus.com/常用软件网站 idea : https://www.jetbrains.com/idea/downloa ...

  4. Introduction of Machine Learning

    李宏毅主页 台湾大学语音处理实验室 人工智慧.机器学习与深度学习间有什么区别? 人工智能——目标 机器学习——手段 深度学习——机器学习的一种方法 人类设定好的天生本能 Machine Learnin ...

  5. LeetCode--字符串

    1.给定字符串s,分区s使得分区的每个子字符串都是回文. 返回s的所有可能的回文分区.例如,给定s =“aab”,返回 [ ["aa","b"], [" ...

  6. C C++输出格式 <转载>仅用于个人

    转载链接:C++ C C语言输出格式总结 1 一般格式    printf(格式控制,输出表列)    例如:printf("i=%d,ch=%c\n",i,ch);    说明: ...

  7. inotify+rsync文件实时同步

    原文转自http://dl528888.blog.51cto.com/2382721/771533/ 之前做了“ssh信任与scp自动传输脚本”的技术文档,此方案是作为公司里备份的方法,但在实际的运行 ...

  8. 模块化开发之sea.js

    随着时间的推移,原生js越来越强大,es6中的improt,export已经可以实现模块化开发,但可惜的是现在的浏览器还不支持,需要进行编译,相信在不久的将来,一定会大行其道,今天我们来聊聊模块化开发 ...

  9. c++初步认识

    经过漫长的C学习终于踏入C++的知识库当中了,还是保持以前的习惯会一步步通过写博客的形式来记录点滴学习记录,这种学习方式是相当慢的,但是对我来说是最踏实的,不浮躁,一步一个脚印.C++是一门啥语言呢, ...

  10. python_tkinter事件

    1.事件绑定函数(3个) 组件.bind('事件类型',事件函数) 为一个组件绑定一个操作 组件.bind_class('组件类型','事件类型',事件函数) 为一个类组件绑定一个操作 组件.bind ...