内核workqueue框架

workqueue驱动的底半部实现方式之一就是工作队列,作为内核的标准模块,它的使用接口也非常简单,schedule_work或者指定派生到哪个cpu的schedule_work_on。

还有部分场景会使用自定义的workqueue,这种情况会直接调用queue_work和queue_work_on接口。

static inline bool schedule_work_on(int cpu, struct work_struct *work)
{return queue_work_on(cpu, system_wq, work);        //workqueue_struct使用通用的system_wq
}

虽然使用起来简单,但其实内核内部的整个workqueue框架还是比较复杂的。

后面摘录一些关键性的代码,辅助理解上面的框架:

1、queue_work接口如何选择pwq

如果workqueue使能了WQ_UNBOUND标识,则根据当前cpu选择一个numa node。unbounded pwq是已numa node来划分的。

如果是普通的workqueue,则按照用户指定的cpu(没有指定则使用当前cpu)找到相应的pwq。

	/* pwq which will be used unless @work is executing elsewhere */if (wq->flags & WQ_UNBOUND) {if (req_cpu == WORK_CPU_UNBOUND)cpu = wq_select_unbound_cpu(raw_smp_processor_id());pwq = unbound_pwq_by_node(wq, cpu_to_node(cpu));} else {if (req_cpu == WORK_CPU_UNBOUND)cpu = raw_smp_processor_id();pwq = per_cpu_ptr(wq->cpu_pwqs, cpu);}

2、queue work时work struct挂载到哪个队列

1)当前pwq里pending的任务数量不多,直接挂载到worker pool的任务链表等待执行

2)pending的任务大于pwq的最大活跃数,先挂载到pwq的inactive_works链表;

	if (likely(pwq->nr_active < pwq->max_active)) {trace_workqueue_activate_work(work);pwq->nr_active++;worklist = &pwq->pool->worklist;        //pending任务不多,直接挂载到worker pool的任务链表等待执行if (list_empty(worklist))pwq->pool->watchdog_ts = jiffies;} else {work_flags |= WORK_STRUCT_INACTIVE;worklist = &pwq->inactive_works;    //pending的任务大于最大活跃数,则挂载到pwq的inactive_works链表;}

2、初始化pwq的worker_pool

alloc workqueue时,如何指定worker_pool?

1)per-cpu pwq和per-cpu worker pool

如果没有使能WQ_UNBOUND标识,则申请per-cpu pwqs,链接到per-cpu work_pools。

注意1:并不是为每个workqueue/pwq新创建了worker_pool。不管创建多少workqueue,他们指向的都是一组worker_pool。

那有人可能会想,如果有很多工作队列,只有一组worker_pool忙得过来吗?

其实worker_pool定义的是相同属性的一类工作者线程,比如工作在哪个cpu上/以哪个优先级运行/绑定哪个numa(unbounded绑定numa)。worker_pool中链表维护的worker才对应单个工作者线程。worker thread运行期间会根据当前的忙闲动态创删新的worker,动态启动和停止新的worker thread。

注意2:对于per-cpu worker_pool来说,每个cpu其实对应两个worker_pool,分别是highpri和lowpri,nice值分别为-20和0。实际配置给pwq的是高优先级worker_pool。

static int alloc_and_link_pwqs(struct workqueue_struct *wq)
{if (!(wq->flags & WQ_UNBOUND)) {wq->cpu_pwqs = alloc_percpu(struct pool_workqueue);    //per_cpu pwqif (!wq->cpu_pwqs)return -ENOMEM;for_each_possible_cpu(cpu) {struct pool_workqueue *pwq =per_cpu_ptr(wq->cpu_pwqs, cpu);struct worker_pool *cpu_pools =        //从cpu_worker_pools找到worker_poolper_cpu(cpu_worker_pools, cpu);init_pwq(pwq, wq, &cpu_pools[highpri]);    //链接到高优先级的worker_poolmutex_lock(&wq->mutex);link_pwq(pwq);mutex_unlock(&wq->mutex);}return 0;}/* 申请unbouned pwq*/
}

2)unbounded pwq和unbounded worker pool

使能了WQ_UNBOUND标识,则分配unbounded pwq。并从全局unbound_pool_hash中找到一个attrs属性指定的work_pool,如果没有会新建一个,将这个worker_pool配置给pwq。

alloc_and_link_pwqs  //先申请per cpu pwq,后申请unbounded pwq--->apply_workqueue_attrs--->apply_wqattrs_prepare--->alloc_unbound_pwqstatic struct pool_workqueue *alloc_unbound_pwq(struct workqueue_struct *wq,const struct workqueue_attrs *attrs)
{struct worker_pool *pool;struct pool_workqueue *pwq;lockdep_assert_held(&wq_pool_mutex);pool = get_unbound_pool(attrs);    //找到相同attrs属性的worker_pool,没有则新建一个if (!pool)return NULL;pwq = kmem_cache_alloc_node(pwq_cache, GFP_KERNEL, pool->node);if (!pwq) {put_unbound_pool(pool);return NULL;}init_pwq(pwq, wq, pool);    //pwq->pool = poolreturn pwq;
}

3、创建worker pool

1)per cpu的worker pool

per cpu的worker pool是一个全局性的初始化,在workqueue_init_early接口中实现。这明显是kernel_init启动过程中调用的初始化接口。

void __init workqueue_init_early(void)
{/* initialize CPU pools */for_each_possible_cpu(cpu) {struct worker_pool *pool;i = 0;for_each_cpu_worker_pool(pool, cpu) {    //遍历每个worker poolBUG_ON(init_worker_pool(pool));    //初始化worker poolpool->cpu = cpu;cpumask_copy(pool->attrs->cpumask, cpumask_of(cpu));pool->attrs->nice = std_nice[i++];pool->node = cpu_to_node(cpu);/* alloc pool ID */mutex_lock(&wq_pool_mutex);BUG_ON(worker_pool_assign_id(pool));mutex_unlock(&wq_pool_mutex);}}
}

2)unbounded worker pool

前面讲为pwq链接worker pool的时候提过,unbounded worker pool是根据动态按需分配的。就是在使用的时候,如果已经创建了相同属性的worker pool,则使用;没有则新建一个。

static struct worker_pool *get_unbound_pool(const struct workqueue_attrs *attrs)
{/* 已有同属性的worker pool,则返回*//* nope, create a new one */pool = kzalloc_node(sizeof(*pool), GFP_KERNEL, target_node);    //没有则创建if (!pool || init_worker_pool(pool) < 0)    //初始化worker poolgoto fail;}

4、创建workqueue

通用的创建接口就是alloc_workqueue。

struct workqueue_struct *alloc_workqueue(const char *fmt,unsigned int flags,int max_active, ...)
{--->alloc_and_link_pwqs //申请和初始化pwq,建立pwq和worker pool的链接
}

我们用schedule_work时只需要传入work_struct,并没有指定workqueue。是因为内核提供了一些系统级工作队列,开放给用户直接使用。

比如schedule_work就是直接使用了system_wq。这些工作队列也是在workqueue_init_early注册的。

void __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);
}

我们最常用的是system_wq。内核按需申请了不同类型的workqueue,也有一些位置会专门使用这些workqueue。

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

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

相关文章

探索C++模板类的奥秘:从基础到实践的深度之旅

目录 引言 一、模板类基础 A. 何为模板类&#xff1f; B. 模板类与普通类的区别 二、模板类实例化及特化 C. 实例化模板类 D. 模板类特化 三、模板类的应用与实战 E. 示例分析&#xff1a;栈模板类实现 结论 引言 在C编程的世界里&#xff0c;模板这一特性如同魔法般…

wpf转换器

WPF&#xff08;Windows Presentation Foundation&#xff09;中的转换器主要是指IValueConverter接口的实现&#xff0c;它用于在数据绑定过程中转换源数据和目标数据的类型或表示形式。这种机制使得开发者能够灵活地处理数据&#xff0c;特别是在用户界面&#xff08;UI&…

基于SkyEye运行Android——应用最为广泛的移动设备操作系统

01.Android简介 Android&#xff08;安卓&#xff09;是一种基于Linux内核&#xff08;不包含GNU组件&#xff09;的开源操作系统&#xff0c;最初由安迪鲁宾开发&#xff0c;主要支持手机。2005年8月由Google收购注资&#xff1b;2007年11月&#xff0c;Google与84家硬件制造…

【C语言】/*函数栈帧的创建和销毁*/

目录 前言 一、知识补充 二、分析创建和销毁的过程 三、前言问题回答 前言 本篇主要讨论以下问题&#xff1a; 1. 编译器什么时候为局部变量分配的空间 2. 为什么局部变量的值是随机的 3. 函数是怎么传参的&#xff0c;传参的顺序是怎样的 4. 形参和实参是什么关系 5. 函数…

vue3--element-plus-抽屉文件上传和富文本编辑器

一、封装组件 article/components/ArticleEdit.vue <script setup> import { ref } from vue const visibleDrawer ref(false)const open (row) > {visibleDrawer.value trueconsole.log(row) }defineExpose({open }) </script><template><!-- 抽…

SSM+Vue+小程序+基于微信小程序的高校学生事务管理系统

项目配合学校日常生活通知&#xff0c;考试等管理需要&#xff0c;开发学校事务管理系统&#xff0c;maven管理依赖&#xff0c;mybatis处理数据库交互 学校管理 学院管理 班级管理 年级管理 教师管理 通知公告管理 学生资料管理 待办事项管理 教务处通知管理 讲座通…

OpenCV(一) —— OpenCV 基础

1、OpenCV 简介 OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个基于 BSD 许可开源发行的跨平台的计算机视觉库。可用于开发实时的图像处理、计算机视觉以及模式识别程序。由英特尔公司发起并参与开发&#xff0c;以 BSD 许可证授权发行&#xff0c…

网络知识点之—QoS

QoS&#xff08;Quality of Service&#xff0c;服务质量&#xff09;指一个网络能够利用各种基础技术&#xff0c;为指定的网络通信提供更好的服务能力&#xff0c;是网络的一种安全机制&#xff0c; 是用来解决网络延迟和阻塞等问题的一种技术。QoS的保证对于容量有限的网络来…

C++11:右值引用和左值引用

1.decltype decltype可以推导出一个表达式的类型&#xff0c;可以用这个类型定义变量。 测试代码 template<class T1,class T2> void test1(T1 &a,T2 & b) {decltype(a* b) ret;cout << typeid(ret).name() << endl; } 2.范围for void test2()…

上位机图像处理和嵌入式模块部署(树莓派4b部署java环境)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 通常我们都会认为java是部署在pc服务器上面的&#xff0c;或者是用java开发android应用程序。其实不然&#xff0c;java也可以部署在嵌入式开发板子…

前端工程化03-贝壳找房项目案例JavaScript常用的js库

4、项目实战&#xff08;贝壳找房&#xff09; 这个项目包含&#xff0c;基本的ajax请求调用,内容的渲染&#xff0c;防抖节流的基本使用&#xff0c;ajax请求工具类的封装 4.1、项目的接口文档 下述接口文档&#xff1a; 简述内容baseURL&#xff1a;http://123.207.32.32…

ESP32 烧录固件

第一步&#xff1a;下载固件 git clone --recursive https://github.com/espressif/esp-at.git 第二步&#xff1a;执行编译 在该目录执行 python build.py install 如图&#xff1a; 第三步&#xff1a;选择芯片 输入2 第四步&#xff1a;选择固件 输入1 第五步&#…

error LNK2001: 无法解析的外部符号 “__declspec(dllimport) public: __cdecl ......

运行程序时&#xff0c;报如上图所示错误&#xff0c;其中一条是&#xff1a; ReflectionProbe.obj : error LNK2001: 无法解析的外部符号 "__declspec(dllimport) public: __cdecl osg::Object::Object(bool)" (__imp_??0ObjectosgQEAA_NZ) 报这个错误一般是因为…

银行ETL-监管报送

1104报表 1104报表主要包括&#xff1a;资产负债&#xff0c;表外业务、流动性风险、贷款质量、投向行业和地区、重点客户等。 1104报表分类 普通报表、机构特色类报表。 反洗钱 大额交易、可疑交易。标签分类&#xff1a;疑似犯罪、疑似毒品、疑似传销。 反洗钱—接口报…

揭秘Fabric交易流程:一文带你深入了解

随着区块链技术的日益普及&#xff0c;Hyperledger Fabric作为一种联盟链解决方案&#xff0c;受到了广泛关注。那么&#xff0c;Fabric的交易流程究竟是怎样的呢&#xff1f;本文将为您一一揭晓。 1. Fabric交易的参与方 客户端&#xff1a;交易流程的发起方&#xff0c;发起…

Windows 容器镜像踩坑记录

为什么研究windows容器&#xff1f;emm&#xff0c;公司需要&#xff0c;不想多说。 dotnet后端 问题描述&#xff1a; 基于mcr.microsoft.com/dotnet/aspnet:6.0镜像撰写dockerfile编译.net core后端项目后运行容器出现类库不存在问题&#xff1a; 程序中使用了fastreport&a…

数据库(MySQL)—— DQL语句(基本查询和条件查询)

数据库&#xff08;MySQL&#xff09;—— DQL语句&#xff08;基本查询和条件查询&#xff09; 什么是DQL语句基本查询查询多个字段字段设置别名去除重复记录 条件查询语法条件 我们今天进入MySQL的DQL语句的学习&#xff1a; 什么是DQL语句 MySQL中的DQL&#xff08;Data Q…

【论文】关于网页上能打开的文章下载PDF“显示无效或损坏的 PDF 文件”的解决办法

1. 遇到的问题 今天我在 dl.acm.org/doi 下载论文时发现下载后的pdf打开出现“显示无效或损坏的 PDF 文件” 可是在原网址是可以打开并显示的 2. 解决方案 这里我用到了和之前【论文】去除PDF论文行号的完美解决方案 的相似的解决办法 就是下载的时候不直接下载&#xf…

【前端项目——分页器】手写分页器实现(JS / React)

组件介绍 用了两种方式实现&#xff0c;注释详细~ 可能代码写的不够简洁&#xff0c;见谅&#x1f641; 1. 包含内容显示的分页器 网上看了很多实现&#xff0c;很多只有分页器部分&#xff0c;没和内容显示联动。 因此我增加了模拟content的显示&#xff0c;这里模拟了32条数…

环形链表面试题详解

A. 环形链表1 给你一个链表的头节点 head &#xff0c;判断链表中是否有环. 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置…