linux 内核工作队列

工作队列( work queue)是使用内核线程异步执行函数的通用机制。
工作队列是中断处理程序的一种下半部机制,中断处理程序可以把耗时比较长并且可能睡眠的函数交给工作队列执行。
工作队列不完全是中断处理程序的下半部。内核的很多模块需要异步执行函数,这些模块可以创建一个内核线程来异步执行函数。但是,如果每个模块都创建自己的内核线程,会造成内核线程的数量过多,内存消耗比较大,影响系统性能。所以,最好的方法是提供一种通用机制,让这些模块把需要异步执行的函数交给工作队列执行,共享内核线程,节省资源。
所以 Workqueue 的主要设计思想:一个是并行,多个 work 不要相互阻塞;另外一个是节省资源,多个 work 尽量共享资源 ( 进程、调度、内存 ),不要造成系统过多的资源浪费。
为了实现的设计思想,workqueue 的设计实现也更新了很多版本。最新的 workqueue 实现叫做 CMWQ(Concurrency Managed Workqueue)并发可管理工作队列,也就是用更加智能的算法来实现“并行和节省”。新版本的 workqueue 创建函数改成 alloc_workqueue(),旧版本的函数 create_workqueue() 逐渐会被被废弃。


内核使用工作项保存需要异步执行的函数,工作项的数据类型是 work_struct,需要异步执行的函数的原型如下所示:
struct work_struct {
    atomic_long_t data;
    struct list_head entry;
    work_func_t func;
#ifdef CONFIG_LOCKDEP
    struct lockdep_map lockdep_map;
#endif
};

typedef void(*work_func_t)(struct work_struct *work)

有一类工作项称为延迟工作项,数据类型是 delayed_work。把延迟工作项添加到工作队列中的时候,延迟一段时间才会真正地把工作项添加到工作队列中。延迟工作项是工作项和定时器的结合,可以避免使用者自己创建定时器。
我们可以使用内核定义的工作队列,也可以自己创建专用的工作队列。内核定义了以下工作队列:
include/linux/workqueue.h
extern struct workqueue_struct *system_wq;
extern struct workqueue_struct *system_highpri_wq;
extern struct workqueue_struct *system_long_wq;
extern struct workqueue_struct *system_unbound_wq;
extern struct workqueue_struct *system_freezable_wq;
extern struct workqueue_struct *system_power_efficient_wq;
extern struct workqueue_struct *system_freezable_power_efficient_wq;

kernel/workqueue.c
int __init workqueue_init_early(void)
{
......
    system_wq = alloc_workqueue("events", 0, 0);
    system_highpri_wq = alloc_workqueue("events_highpri", WQ_HIGHPRI, 0);
    system_long_wq = alloc_workqueue("events_long", 0, 0);
    system_unbound_wq = alloc_workqueue("events_unbound", WQ_UNBOUND,
                        WQ_UNBOUND_MAX_ACTIVE);
    system_freezable_wq = alloc_workqueue("events_freezable",
                          WQ_FREEZABLE, 0);
    system_power_efficient_wq = alloc_workqueue("events_power_efficient",
                          WQ_POWER_EFFICIENT, 0);
    system_freezable_power_efficient_wq = alloc_workqueue("events_freezable_power_efficient",
                          WQ_FREEZABLE | WQ_POWER_EFFICIENT,
                          0);
    BUG_ON(!system_wq || !system_highpri_wq || !system_long_wq ||
           !system_unbound_wq || !system_freezable_wq ||
           !system_power_efficient_wq ||
           !system_freezable_power_efficient_wq);

    return 0;
}

system_wq:如果工作项的执行时间比较短,应该使用这个工作队列。早期的内核版本只提供了这个工作队列, 称为全局工作队列, 函数 schedule_work()和 schedule_delayed_work()使用这个工作队列。
system_highpri_wq:高优先级的工作队列。
system_long_wq:如果工作项的执行时间比较长,应该使用这个工作队列。
system_unbound_wq:这个工作队列使用的内核线程不绑定到某个特定的处理器。
system_freezable_wq:这个工作队列可以冻结。
system_power_efficient_wq:如果开启了工作队列模块的参数“ wq_power_efficient”,那么这个工作队列倾向于省电;否则和 system_wq 相同。
system_freezable_power_efficient_wq:这个工作队列和 system_power_efficient_wq 的区别是可以冻结。

(1)定义工作项。
定义一个静态的工作项,参数 n 是变量名称,参数 f 是工作项的处理函数。
DECLARE_WORK(n, f)
定义一个静态的延迟工作项,参数 n 是变量名称,参数 f 是工作项的处理函数。
DECLARE_DELAYED_WORK(n, f)

使用 DECLARE_DEFERRABLE_WORK(n, f)也可以定义一个静态的延迟工作项,和DECLARE_DELAYED_WORK()的区别是它使用可推迟的定时器( deferrable timer)。
可推迟的定时器在系统忙的时候工作正常,但是在处理器空闲的时候不会处理可推迟的定时器。当一个不可推迟的定时器唤醒处理器的时候,才会处理可推迟的定时器。

在运行时动态初始化工作项,方法如下。
1) INIT_WORK(_work, _func):初始化一个工作项,参数_work 是工作项的地址,参数_func 是需要异步执行的函数。
2) INIT_WORK_ONSTACK(_work, _func):初始化一个工作项,工作项是栈里面的局部变量,参数_work 是工作项的地址,参数_func 是需要异步执行的函数。
3) INIT_DELAYED_WORK(_work, _func):初始化一个延迟工作项,参数_work 是延迟工作项的地址,参数_func 是需要异步执行的函数。
4) INIT_DELAYED_WORK_ONSTACK(_work, _func):初始化一个延迟工作项,延迟工作项是栈里面的局部变量,参数_work 是延迟工作项的地址,参数_func 是需要异步执行的函数。
5) INIT_DEFERRABLE_WORK(_work, _func):初始化一个延迟工作项,和 INIT_DELAYED_WORK()的区别是它使用可推迟的定时器。
6) INIT_DEFERRABLE_WORK_ONSTACK(_work, _func):初始化一个延迟工作项,延迟工作项是栈里面的局部变量,和 INIT_DELAYED_WORK_ONSTACK()的区别是它使用可推迟的定时器。

(2)全局工作队列。
在全局工作队列中添加一个工作项。
bool schedule_work(struct work_struct *work);
在全局工作队列中添加一个工作项,并且指定执行工作项的处理器。
bool schedule_work_on(int cpu, struct work_struct *work);
在全局工作队列中添加一个延迟工作项,参数 delay 是把工作项添加到工作队列中之前等待的时间,单位是嘀嗒( tick)。
bool schedule_delayed_work(struct delayed_work *dwork, unsigned long delay);
在全局工作队列中添加一个延迟工作项,并且指定执行工作项的处理器。
bool schedule_delayed_work_on(int cpu, struct delayed_work *dwork,unsigned long delay);
冲刷全局工作队列,确保全局工作队列中的所有工作项执行完。
void flush_scheduled_work(void);
(3)专用工作队列。
分配工作队列的函数是:
alloc_workqueue(fmt, flags, max_active, args...)
1)参数 fmt 是工作队列名称的格式。
2)参数 flags 是标志位,可以是 0,也可以是下面这些标志位的组合。
 WQ_UNBOUND:处理工作项的内核线程不绑定到任何特定的处理器。
 WQ_FREEZABLE:在系统挂起的时候冻结。
 WQ_MEM_RECLAIM:在内存回收的时候可能使用这个工作队列。
 WQ_HIGHPRI:高优先级。
 WQ_CPU_INTENSIVE:处理器密集型。
 WQ_POWER_EFFICIENT:省电。
3)参数 max_active 是每个处理器可以同时执行的工作项的最大数量, 0 表示使用默认值256。
4)参数 args 是传给参数 fmt 的参数。

下面的函数用来分配一个有序的工作队列。有序的工作队列在任何时刻,按照入队的顺序只执行一个工作项。
alloc_ordered_workqueue(fmt, flags, args...)

#define alloc_ordered_workqueue(fmt, flags, args...)            \
    alloc_workqueue(fmt, WQ_UNBOUND | __WQ_ORDERED |        \
            __WQ_ORDERED_EXPLICIT | (flags), 1, ##args)

兼容旧旧接口
#define create_workqueue(name)                      \
alloc_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, 1, (name))
#define create_freezable_workqueue(name)                \
    alloc_workqueue("%s", __WQ_LEGACY | WQ_FREEZABLE | WQ_UNBOUND | \
            WQ_MEM_RECLAIM, 1, (name))
#define create_singlethread_workqueue(name)             \
    alloc_ordered_workqueue("%s", __WQ_LEGACY | WQ_MEM_RECLAIM, name)

在指定的工作队列中添加一个工作项。
bool queue_work(struct workqueue_struct *wq, struct work_struct *work);
在指定的工作队列中添加一个工作项,并且指定执行工作项的处理器。
bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work);
在指定的工作队列中添加一个延迟工作项,参数 delay 是把工作项添加到工作队列中之前等待的时间,单位是嘀嗒( tick)。
bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);
在指定的工作队列中添加一个延迟工作项,并且指定执行工作项的处理器。
bool queue_delayed_work_on(int cpu, struct workqueue_struct *wq,struct delayed_work *work, unsigned long delay);
冲刷工作队列,确保工作队列中的所有工作项执行完。
void flush_workqueue(struct workqueue_struct *wq);
销毁工作队列的函数是:
void destroy_workqueue(struct workqueue_struct *wq);

(4)其他编程接口。
取消一个工作项。
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);
等待一个工作项执行完。
bool flush_work(struct work_struct *work);
等待一个延迟工作项执行完。
bool flush_delayed_work(struct delayed_work *dwork);
判断任务项目是否在进行中
bool work_pending(struct work_struct work );/*返回值为真表示正在运行,假表示停止*/

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

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

相关文章

android NDK相关,调用C库,JNI,交叉编译,跨平台

首先就是库给我,我先进行开发(按照文档),最后进行交叉编译ARM平台。 遇到的问题:我要看到代码效果,所以必须要在真机上跑代码,那么我调用的库就必须是我这个平台适用的,有两种方式&…

热烈欢迎省工信厅毛郑建处长莅临公司调研指导工作

2023年11月28日,河南省工信厅信息化和软件服务业处毛郑建处长莅临郑州埃文计算机科技有限公司(以下简称“埃文科技”)调研考察工作。河南省工业信息安全产业发展联盟理事长任传军陪同调研。 首先,埃文科技董事长王永向毛处长介绍埃…

保存防火墙的规则和自定义链

保存防火墙的规则 将iptables的规则导入开机自启的文件里做脚本使用&#xff0c;就算iptables -F删除&#xff0c;重启就能恢复了 iptables-save #查看当前的所有规则 iptables -save > 保存规则的文件 #保存规则 iptables -restore < 保存的规则文件#重新导入规则 在当…

优维全新低碳产品亮相SBE23 Asia-Pacific绿色建筑促进碳中和论坛

2023年11月23日—24日&#xff0c;由深圳市人民政府主办&#xff0c;深圳市住房和建设局、深圳市发展与改革委员会、深圳市龙岗区人民政府承办&#xff0c;深圳市绿色建筑协会作为执行单位的“2023年可持续建筑环境亚太地区会议&#xff08;SBE23 Asia-Pacific&#xff09;”在…

Ubuntu Server 20.04.6安装Anaconda3

下载安装包 去下面的网页找到自己想要安装的对应版本的链接&#xff1a; https://repo.anaconda.com/archive/ 我安装的版本链接如下&#xff1a; https://repo.anaconda.com/archive/Anaconda3-2023.09-0-Linux-x86_64.sh 复制这个链接后使用如下命令下载&#xff1a; wget …

一篇文章带你掌握MongoDB

文章目录 1. 前言2. MongoDB简介3. MongoDB与关系型数据库的对比4. MongoDB的安装5. Compass的使用6. MongoDB的常用语句7. 总结 1. 前言 本文旨在帮助大家快速了解MongoDB,快速了解和掌握MongoDB的干货内容. 2. MongoDB简介 MongoDB是一种NoSQL数据库&#xff0c;采用了文档…

react hooks学习之useMemo和useCallback

useMemo和useCallback的用法以及区别 useCallback 和 useMemo 是 React 中的两个钩子函数&#xff0c;它们的目标都是优化性能&#xff0c;但它们在用途和使用场景上有一些区别。 useCallback useCallback 用于缓存一个回调函数&#xff0c;并在依赖项发生变化时&#xff0c…

arthas使用

官方文档 Github: https://github.com/alibaba/arthas 文档: https://arthas.aliyun.com/doc/ Arthas 是一款线上监控诊断产品&#xff0c;通过全局视角实时查看应用 load、内存、gc、线程的状态信息&#xff0c;并能在不修改应用代码的情况下&#xff0c;对业务问题进行诊断…

C语言从入门到实战——常用字符函数和字符串函数的了解和模拟实现

常用字符函数和字符串函数的了解和模拟实现 前言1. 字符分类函数2. 字符转换函数3. strlen的使用和模拟实现4. strcpy的使用和模拟实现5. strcat的使用和模拟实现6. strcmp的使用和模拟实现7. strncpy函数的使用8. strncat函数的使用9. strncmp函数的使用10. strstr的使用和模拟…

Json示例

这里写目录标题 CjsonC JSonQT json Cjson #include <stdio.h> #include <stdlib.h> #include "cjson.h" // 向JSON对象中添加数据 cJSON_AddStringToObject(root, "name", "John"); cJSON_AddNumberToObject(root, "age&…

Redis 数据恢复方式说明

AOF日志 AOF(Append Only File)是写后日志,Redis先执行命令把数据写入内存,然后才记录日志。 实现原理 AOF 里记录的是 Redis 收到的每一条命令,这些命令是以文本形式保存的。 示例: set testkey testvalue 对应AOF文件 *3 $3 set $7 testkey $9 testvalue “*3”表示…

MatchPyramid实现文本匹配

引言 今天利用MatchPyramid实现文本匹配。 原论文解析→点此←。 MatchPyramid 核心思想是计算两段文本间的匹配矩阵&#xff0c;把它当成一个图形利用多层卷积网络提取不同层级的交互模式。 匹配矩阵是通过计算两段输入文本基本单元(比如字或词)之间相似度得到的&#xf…

【0241】Parser解析分析统计信息(PARSE ANALYSIS STATISTICS)

1. 执行解析分析 PG内核通过调用pg_parse_query()函数,完成用户查询字符串的解析任务。并返回List类型的参数parsetree_list。如下所示: $4 = {type = T_List, length = 1, max_length = 1, elements = 0x1fc72e0, initial_elements = 0x1fc72e0}之后运行原始解析树并处理每…

Java作业题记录

loading... 1.定义一个Person类{name, age, job},初始化Person对象数组&#xff0c;有3个person对象&#xff0c;并按照 age 从 大到 小进行排序,提示&#xff0c;使用冒泡排序Homework01.java 难点1&#xff1a;初始化数组Person[] people new Person[3]; 难点2&#xff…

43.0BaseDao抽取dao公共父类

43.1. 回顾 1. 把数据库表中查询的结果封装到一个实体类中。 命名规则:类名和表名一致 类中属性和表的字段对应。 表中的一条记录对应实体的一个对象 多条记录→集合 43.2. 正文 目录 43.1. 回顾 43.2. 正文 43.3. 抽取dao公共父类。 43.4. 引入数据源 43.3. 抽取dao公共…

C#测试开源运行耗时库MethodTimer.Fody

微信公众号“dotNET跨平台”的文章《一个监控C#方法运行耗时开源库》介绍了支持测量方法耗时的包MethodTimer.Fody&#xff0c;使用方便&#xff0c;还可以自定义输出信息格式。本文学习并测试MethodTimer.Fody包的使用方式。   新建控制台程序&#xff0c;通过Nuget包管理器…

Python链式调用技巧:代码流畅无缝连接

更多资料获取 &#x1f4da; 个人网站&#xff1a;ipengtao.com 链式调用是一种编程风格&#xff0c;它允许将多个方法调用连接在一起&#xff0c;形成一个连贯的操作链。在Python中&#xff0c;链式调用常常用于使代码更简洁、易读&#xff0c;尤其在处理数据处理和函数式编程…

web:ics-05(本地文件包含漏洞、preg_replace函数/e漏洞、php伪协议读取文件)

题目 打开页面显示如下 只有这个页面能打开 显示如下 用dirsearch扫一下 查看了一下&#xff0c;发现没什么用 查看页面源代码 返回了&#xff0c;写入的参数&#xff0c;猜测可能有文件包含漏洞 用php伪协议读取文件 构造payload ?pagephp://filter/readconvert.base64-en…

WordPress定时文章自动发布技巧

对于许多WordPress站长来说&#xff0c;文章的管理和发布计划往往是一个头疼的问题。随着内容的不断增加&#xff0c;时间表的调整以及发布频率的把握成为了让人焦头烂额的挑战。 一、时间管理难题 对于博客管理员来说&#xff0c;时间管理一直是个令人困扰的问题。在忙碌的生…

Springboot-注册注解【springboot常用注解】

1.组件注册 1.1 使用的注解 Configuration:普通配置类,替代以前的配置文件,配置类本身也是容器的组件|SpringBootConfiguration:Springboot配置类,与Configuration功能一样|Bean:替代以前的Bean标签,如果没有在Bean标签内定义名字,则默认组件的名字为方法名,可以直接修改注解…