linux内核多队列,Linux Kernel 中 Workqueue 使用系统默认队列和创建队列的方法

关于workqueue,我们还是有很多话要说。

想必大家对workqueue相关的函数(schedule_work 、queue_work、INIT_WORK、create_singlethread_workqueue 等)都不陌生。但说起差异,可能还有许多话需要坐下来慢慢讲。

对于workqueue,与之最为相关的两个东西便是work和queue。

work是用来绑定实际执行函数使用的结构体

queue是用来链接work使用的队列。

具体的结构体,可以自己到/kernel/include/linux/workqueue.h中自行查看,这里不再赘述。

我们想关注的重点在于:

1:系统中是否有default的workqueue供我们使用

2:我们能否创建自己的workqueue?如何创建?

Yes!你说对了。linux系统所提供的workqueue机制中,已经帮忙提供了一个默认的workqueue队列“system_wq”,并提供了一套函数来方便大家直接使用。

例子来了:

static struct work_struct work;

INIT_WORK(&work, run);

schedule_work(&work);

static void run(struct work_struct *work)

{

Do something here!!

}

就这么简单的,当然,你也可以用DECLARE_WORK来完成和INIT_WORK同样初始化work的工作。区别是DECLARE_WORK是预编译命令,而INIT_WORK可以在code中动态初始化。

那么除了调用schedule_work直接把work放到系统default的workqueue中外,我们还有什么办法可以初始化自己的workqueue,并且放入work呢?

我们看看函数schedule_work的定义,一切就真相大白了!

static inline bool schedule_work(struct work_struct *work)

{

return queue_work(system_wq, work);

}

哈哈,原来schedule_work是把传入的work直接放入了系统的default workqueue “system_wq”中而已。

自然,我们只需要调用queue_work函数来绑定workqueue和work就ok啦!

初始化work的方法和前面一样,只要调用DECLARE_WORK或INIT_WORK就好了。

那么我们如何去创建自己的workqueue呢?

答案是:

#define alloc_ordered_workqueue(fmt, flags, args…)               \

alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED | (flags), 1, ##args)

#define create_workqueue(name)                              \

alloc_workqueue((name), WQ_MEM_RECLAIM, 1)

#define create_freezable_workqueue(name)                    \

alloc_workqueue((name), WQ_FREEZABLE | WQ_UNBOUND | WQ_MEM_RECLAIM, 1)

#define create_singlethread_workqueue(name)                    \

alloc_workqueue((name), WQ_UNBOUND | WQ_MEM_RECLAIM, 1)

我们只要调用如上几种方法的某一种,即可创建属于自己的workqueue了。kernel中最常见到的函数是create_singlethread_workqueue。

我们只要拿这个函数举个例子就美好了,栗子来了!

static struct workqueue_struct *time_sync_wq;

time_sync_wq = create_singlethread_workqueue(“timesync“); //timesync就是workqueue的名字

static DECLARE_WORK(etr_work, etr_work_fn);

queue_work(time_sync_wq, &etr_work);

这样一来,我们的work和自己的workqueue就绑在一起了。

个人认为,自己新建wq最大的好处:可以避免system_wq中被挂的work过多,或者由于某个被挂上去的work处理函数质量不高导致死锁,而导致挂在同一个queue上的我们自己work的handler因为无法被调度到而完蛋了。

当然,建太多自己的workqueue,必然会导致系统调度开销的变大,所以需要取舍。

至于DELAYED_WORK

#define INIT_DELAYED_WORK(_work, _func)                         \

__INIT_DELAYED_WORK(_work, _func, 0)

queue_delayed_work

schedule_delayed_work

都和前面类似,就不再赘述了。

补充下:

咱们可以调用cancel_delayed_work来把还未执行的work给取消掉。

基本上每次 cancel_delayed_work 之后您都得调用flush_scheduled_work() 这个函数 , 特别是对于内核模块 , 如果一个模块使用了工作队列机制 , 并且利用了系统的default队列 , 那么在卸载这个模块之前 , 您必须得调用这个函数 , 这叫做刷新一个工作队列 , 也就是说 , 该函数会一直等待 , 直到队列中所有对象都被执行以后才返回 ,从而避免队列调度错误。

函数cancel_delayed_work_sync的出现,让新的流程变得更加简单了,大家参照kernel中的代码,很容易知道应该怎么用。

最后别忘了调用destroy_workqueue等清尾的函数哦~~

问题来了:

如果我们在handler的执行过程中,同时再次调用调度函数queue_work,那么我们的handler会被执行多少次呢?(执行被调度的次数,还是就只执行一次呢?)

解答:

这个问题比较有意思,写了这个例子来验证答案(例子跑在android 4.4 code base中,不排除后续kernel函数被修改)

sample test code:

static struct workqueue_struct *test_wq;

static void try_to_test(struct work_struct *work){printk(“[bevis] :wq into \n”);

msleep(5*1000);  //5s

printk(“[bevis] :wq out \n”);

}

static DECLARE_WORK(mytest_work, try_to_test);

gsensor probe function end add :

test_wq =alloc_ordered_workqueue(“test_wq”, 0);//初始化一个单独的工作队列

int a = 0;

for(a=0 ; a<3 ; a++){

printk(“[bevis] : read func (%d) before \n”,a);

queue_work(test_wq, &mytest_work);//让这个队列开始被调度

printk(“[bevis] : read func (%d) msleep 2s \n”,a);

msleep(2*1000);

printk(“[bevis] : read func (%d) after \n”,a);

}

log如下:

10-16 14:10:41.940 I/KERNEL  (  109): [    6.954658] [bevis] : read func (0) before

10-16 14:10:41.940 I/KERNEL  (  109): [    6.954727] [bevis] : read func (0) msleep 2s

10-16 14:10:41.940 I/KERNEL  (  109): [    6.954742] [bevis] :wq into

10-16 14:10:43.950 I/KERNEL  (  109): [    8.960997] [bevis] : read func (0) after

10-16 14:10:43.950 I/KERNEL  (  109): [    8.961085] [bevis] : read func (1) before

10-16 14:10:43.950 I/KERNEL  (  109): [    8.961155] [bevis] : read func (1) msleep 2s

10-16 14:10:45.960 I/KERNEL  (  109): [   10.971954] [bevis] : read func (1) after

10-16 14:10:45.960 I/KERNEL  (  109): [   10.972076] [bevis] : read func (2) before

10-16 14:10:45.960 I/KERNEL  (  109): [   10.972132] [bevis] : read func (2) msleep 2s

10-16 14:10:46.950 I/KERNEL  (    6):  [   11.961884] [bevis] :wq out

10-16 14:10:46.950 I/KERNEL  (    6):  [   11.961953] [bevis] :wq into

10-16 14:10:47.970 I/KERNEL  (  109): [   12.982276] [bevis] : read func (2) after

10-16 14:10:51.960 I/KERNEL  (    6):  [   16.973719] [bevis] :wq out

看到了吧,虽然我们使用queue_work函数调度了三次handler,但实际上wq的handler只被执行了两次。

如果把probe函数的delay直接拿掉,你更加会发现,即使wq被调度三次,handler却实际上只跑了一次。

结论:

如果wq被调度的时候,wq中的这个handler正在执行过程中,则这次调度会被遗弃。只有handler执行完成并返回后,下次调度才会真正的生效。

kernel这么做的原因,我猜想应该是为了防止,档某个wq的handler在执行过程中因为资源无法获取而暂时阻塞时,

不会因为其他进程再次调度了该wq而导致出现线程实例的不断累加。

实际上,在绝大多数情况下,我们只需要一个handler实例来帮忙做事就够了,例如earlysuspend的处理函数中,只要userspace进行想睡眠,那就直接调度suspend wq的handler,而不必管再关心上次的suspend过程是否有阻塞。

这样一来,逻辑就清爽多了。

如需转载,请注明出处。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/409395.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

linux扩容根目录空间_Linux系统扩容根目录磁盘空间的操作方法

Linux系统扩容根目录磁盘空间的操作方法一、使用背景Linux根目录磁盘空间不够用了&#xff0c;当修改了虚拟机模版增加磁盘大小或者插入了一块新硬盘&#xff0c;但是发现系统里的大小还是没改变。产生的原因是没有给磁盘格式化&#xff0c;没有增加分区。二、操作方法1.查看磁…

【ASP】文件上传

描述&#xff1a;在button事件中注册一个上传事件 代码详解&#xff1a; protected void FileUpload_Click(object sender, EventArgs e) { bool fileIsvaild false; //初始化一个bool型为false的fileIsvaild if (this.FileUpload1.HasFile)…

哈夫曼字符串编码c语言实现,基于哈夫曼(haffuman)算法的文件压缩的实现(C语言)(原创)...

本文首先简要阐述哈夫曼算法的基本思想&#xff0c;然后介绍了使用哈夫曼算法进行文件压缩和解压缩的处理步骤&#xff0c;最后给出了C语言实现的文件压缩和解压缩的源代码。哈夫曼算法的主要思想是&#xff1a;①首先遍历要处理的字符串&#xff0c;得到每个字符的出现的次数&…

html 长文本 截断 jquery 扩展脚本

(function ($){$.LongTextFormat function (selector, fmlength){var re /\s/g;$(selector).each(function (i){//获取td当前对象的文本,如果长度大于25; var jobj $(this);var text jobj.text();if (text){text text.replace(re, "");if (text.length > fml…

rn 滑动验证_rn-fetch-blob

1.前言 最近开发react-native的app,上传图片功能需要使用相机,发现Genymotion默认的相机不工作.查看同行的博客解决了,归纳整理一下. 2.步骤 2.1安装Genymotion:http://www.genymotion.net/ 2.2在windows电脑上安装ManyCam ...ziguiyu2019-05-15React Native最近有大动作&#…

md5生成一个加盐程序c语言,MD5在编程中的实现 (C语言)

在C语言中实现MD5算法———————————————————————————————————————————*/#ifndef PROTOTYPES#define PROTOTYPES 0#endiftypedef unsigned char *POINTER;typedef unsigned short int UINT2;typedef unsigned long int UINT4;#if PROT…

用matlab画大数据曲线_基于MATLAB的大数据分析

王媚摘要&#xff1a;传统计算机模式与MATLAB软件技术相比较&#xff0c;传统软件运行起来较为复杂。以此基于MATLAB软件下的网络数据技术&#xff0c;它以高速化、关联化的优势成为人们眼中的焦点。本文针对传统网络软件模式中出现的问题&#xff0c;对基于MATLAB大数据技术进…

代码之谜(三)- 运算符

从最简单的运算符加号()说起&#xff0c;加号()是个二元运算符——也就是说&#xff0c;加号只把两个数联接起来&#xff0c;从来不把第三个或者更多的联接起来。 因此&#xff0c;“1加2加3” 在计算机中被表述为&#xff1a; (1 2) 3 // a 或者 1 (2 3) // b 虽…

非培训的前端转行之路(根据个人真实经历)

我是歌谣 放弃很很难 但是坚持一定很酷 本文乃本人真实经历书写 希望对你的工作和学习有所帮助 感谢你得阅读 前言 我是歌谣&#xff0c;当然真名不是叫歌谣。 歌谣的原因 1. 歌谣可以传承很久 影响比较大 2. 歌谣可以让人心情愉悦 让人积极向上 3. 名字里面有个谐音 哈哈 …

c语言做贪吃蛇vs2015,熬书几个月,终于编出简易的贪吃蛇了,VS2013

该楼层疑似违规已被系统折叠 隐藏此楼查看此楼#include#include#include#include#define X 30#define Y 15void guozi(int *x, int *y);int main(void){char map[X][Y];int x;int y;//横纵坐标int i;int j;//标记蛇头int p, q;//标记蛇尾int t, d;//寻找蛇尾int n 4;//蛇的长度…

java零钱换整程序_贪心算法换零钱(java)

贪心算法思想贪心算法总是做出在当前看来做好的选择。也就是说贪心算法并不从整体最后考虑&#xff0c;他做出的选择只是局部最优选择。他所做出的仅是在某种意义上的局部最优解。贪心算法不是对所有问题都能得到整体最优解&#xff0c;但对范围相当广泛的许多问题他能产生整体…

[LeetCode] Sqrt(x)

Implement int sqrt(int x). Compute and return the square root of x. 牛顿迭代 1 class Solution {2 public:3 int sqrt(int x) {4 // Start typing your C/C solution below5 // DO NOT write int main() function6 double ans x;7 8…

c语言江宝钏实验六答案,C语言程序设计江宝钏著实验六答案

C语言程序设计江宝钏著实验六答案6.4 实验六 数组一、 实验目的与要求1. 掌握一维数组的定义、赋值和输入输出的方法。2. 理解一维数组的存储方法及下标的表示方法。3. 掌握与数组有关的算法(特别是排序算法 )。4. 掌握二维数组的定义、赋值和输入输出的方法。5. 理解二维数组的…

一文带你理解vue创建一个后台管理系统流程(Vue+Element)

我是歌谣 放弃很容易 但是坚持一定很酷 1前言 本文根据自己工作经历编写&#xff0c;若有不合理之处&#xff0c;欢迎吐槽 2定义 后台管理系统什么 对一个页面进行增删改查 是不是有点像&#xff0c;不重复定义 3第一次接触后台管理系统 第一次接触后台管理系统是某b站的一…

glibc的头文件 linux_求助,编译glibc头文件时出错

我用的软件包如下:binutils-2.16.tar.gzgcc-3.4.4.tar.bz2glibc-2.3.5.tar.gzglibc-linuxthreads-2.3.5.tar.gzlinux-2.6.14.1.tar.gz补丁:ioperm.c.diffflow.c.difft-linux.diff想编译交叉工具链,用的编译指令是这样的:[rootlocalhost build-glibc-headers]#../glibc-2.3.5/co…

多表查询的一些技巧

1、基本方法&#xff1a;From 后面可以接多个表名&#xff0c;表与表之间用逗号隔开&#xff0c;查询字段之间要加上表的名字。 例如&#xff1a;Select table1.column1, table2.column1 from table1, table2 2、表别名&#xff1a;可以为表设置别名&#xff0c;以简化输入&…

c语言 将结构体放在flash,如何将 结构体 的 数据 定义在flash里面,并如何读出来,求各位达人帮助……...

如题&#xff0c;本人不太熟悉GCC&#xff0c;特别是定义在flash和eeprom的数据&#xff0c;所以在做液晶的字库时遇到问题&#xff0c;讲解下思路&#xff1a;用查表的方式&#xff0c;定义一个结构体来装载字符的内码和字符数据。程序如下&#xff1a;/*********************…

“数万行代码“教你用html和css编写一个精美的网页

我是歌谣 放弃很容易 但是坚持一定很酷 1前言 作为一名前端开发工程师 开发一个完美的网页也是我们的必修课之一 逻辑写起来有时候不是那么的难 据说页面样式才是最难的一课 本文内容纯属自己个人观点 欢迎一起交流吐槽 2网页基础版&#xff08;divcss&#xff09; 我第一次接触…