Linux内核工作队列(workqueue)详解

news2025/2/25 22:01:52

1、为什么需要工作队列?

        在内核代码中,经常会遇到不能或不合适去马上调用某个处理过程,此时希望将该工作推送给某个内核线程执行,这样做的原因有很多,比如:

  • 中断触发了某个过程的执行条件,而该过程执行时间较长或者会调用导致睡眠的函数,则该过程不应该在中断上下文中立即被调用。
  • 类似于中断,一些紧急性的任务不希望执行比较耗时的非关键过程,则需要把该过程提交到低优先级线程执行。比如一个轮询的通信接收线程,它需要快速完成检测和接收数据,而对数据的解析则应该交由低优先级线程慢慢处理。
  • 有时希望将一些工作集中起来以获取批处理的性能;或则合并缩减一些执行线程,减少资源消耗。
  • 基于以上需求,人们开发出了工作队列这一机制。工作队列不光在操作系统内核中会用到,一些应用程序或协议栈也会实现自己的工作队列。

2、工作队列的概念

工作队列 ( workqueue )是将操作(或回调)延期异步执行的一种机制。工作队列可以把工作推后,交由一个内核线程去执行,并且工作队列是执行在线程上下文中,因此工作执行过程中可以被重新调度、抢占、睡眠。

工作项(work item)是工作队列中的元素,是一个回调函数和多个回调函数输入参数的集合,有时也会有额外的属性成员,总之通过一个结构体即可记录和描述一个工作项。

3、工作队列的特性

通过工作队列来执行一个工作相比于直接执行,会有一下特性:

  • 异步,工作不是在本中断或线程中执行,而是交由其他线程执行。
  • 延期,交由低优先级线程执行,执行前可能被抢占,也可能有其他工作排在前面执行,所以从提交工作队列到工作真正被执行之间会延时不定长时间。
  • 排队,FIFO 模式先到先执行。也肯会有多个优先级的工作队列,低优先级的工作队列要等到高优先级的工作队列全部执行完成后才能执行。但同一个工作队列内部的各项都是按时间先后顺序执行的额,不会进行钱赞重排。
  • 缓存,既然是队列它就能缓存多个项,需要异步执行丢进去就行,队列会逐个执行。虽然工作队列能缓存多个项,但也是有上限的当队列已满时,新的入队项就会被丢弃,丢弃的个数会被统计下来。

4、实现 

Linux内核中的工作队列包括:共享工作队列和自定义工作队列。区别如下:

1)共享工作队列:将新创建的工作任务添加到Linux内核创建的全局工作队列system_wq中,无需自己创建工作队列;

2)自定义工作队列:将新创建的工作任务添加到自己创建的工作队列中;

1、共享工作队列举例(demol来源于网络)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>

int data  = 10;
 
static struct work_struct       work1;
static struct work_struct       work2;
 
 
 
static void do_work1(struct work_struct *arg)
{
    printk(KERN_INFO "do_work1 .....");
}
 
static void do_work2(struct work_struct *arg)
{
     printk(KERN_INFO "do_work2 .....");
}
 
int threadfn(void *data)
{
     static int count =  0 ;
     int args = *(int *)data;
     printk(KERN_INFO "enter thead_fn");
     while(1)
     {
         msleep(2*1000);
         printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);
         schedule_work(&work1);
         schedule_work(&work2);
         
     }
} 
 
 
static int __init test_kobj_init(void)
{
 
    INIT_WORK(&work1,do_work1);
    INIT_WORK(&work2,do_work2);
 
    struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");
    if(thread != NULL)
    {
        printk(KERN_INFO "thread create success");
        wake_up_process(thread);
        
    }else
    {
        printk(KERN_ERR "thread create err");
    }
 
     return 0;
}
 
 
 
static void __exit test_kobj_exit(void)
{
     printk(KERN_INFO "test_kobj_exit ");
     return;
}
 
module_init(test_kobj_init);
module_exit(test_kobj_exit);
 
MODULE_AUTHOR("weilaikeji");
MODULE_LICENSE("GPL");

 输出结果

 2、自定义工作队列举例(demol来源于网络)

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/kobject.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <asm/ptrace.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
int data  = 10;

static struct workqueue_struct *workqueue;
static struct work_struct       work1;
static struct work_struct       work2;



static void do_work1(struct work_struct *arg)
{
    printk(KERN_INFO "do_work1 .....");
}

static void do_work2(struct work_struct *arg)
{
    printk(KERN_INFO "do_work2 .....");
}


int threadfn(void *data)
{
    static int count =  0 ;
    int args = *(int *)data;
    printk(KERN_INFO "enter thead_fn");
    while(1)
    {
        msleep(2*1000);
        printk(KERN_INFO "threadfn data: %d, count: %d",args , ++count);
        queue_work(workqueue,&work1);//将任务放到自己创建的工作队列上去执行
        queue_work(workqueue,&work2);
        
    }
} 


static int __init test_kobj_init(void)
{

    
   workqueue = create_workqueue("wanghb_queue");
   INIT_WORK(&work1,do_work1);
   INIT_WORK(&work2,do_work2);

   struct task_struct *  thread =  kthread_create( threadfn,(void * )&data,"mythread");
   if(thread != NULL)
   {
       printk(KERN_INFO "thread create success");
       wake_up_process(thread);
       
   }else
   {
       printk(KERN_ERR "thread create err");
   }

    return 0;
}



static void __exit test_kobj_exit(void)
{
    printk(KERN_INFO "test_kobj_exit ");
    destroy_workqueue(workqueue);
    return;
}

module_init(test_kobj_init);
module_exit(test_kobj_exit);

MODULE_AUTHOR("weilaikeji");
MODULE_LICENSE("GPL");

输出结果

3、执行函数的入参如何传递的?

看到这里,会有个疑问,如何把用户的数据作为参数传递给执行函数呢?

2.6.20版本之后使用工作队列需要把work_struct定义在用户的数据结构中,然后通过container_of来得到用户数据。具体用法如下:

struct my_struct_t { 
    char *name; 
    struct work_struct my_work; 
};

void my_func(struct work_struct *work) 
{ 
    struct my_struct_t *my_name = container_of(work, struct my_struct_t, my_work);         
    printk(KERN_INFO “Hello world, my name is %s!\n”, my_name->name);
} 

struct workqueue_struct *my_wq = create_workqueue(“my wq”); 
struct my_struct_t my_name; my_name.name = “Jack”; 
INIT_WORK(&(my_name.my_work), my_func); 
queue_work(my_wq, &my_work);
destroy_workqueue(my_wq);

在使用如下函数时注意事项

1、flush_work():堵塞工作任务,直到工作任务完成

2、flush_delayed_work():等待延时工作任务完成

3、cancel_work_sync():取消工作任务并等待它完成

4、cancel_delayed_work():取消延时工作任务

5、cancel_delayed_work_sync():取消延时工作任务并等待它完成

6、create_workqueue():对于多CPU系统,内核会在每个CPU上创建一个工作队列,使线程处理并行化

7、create_singlethread_workqueue():内核只在一个CPU上创建一个工作队列

8、queue_work_on():在指定CPU上添加工作任务,queue_work()调用queue_work_on()在所有CPU上添加工作任务

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

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

相关文章

电表485通讯抄表软件

电表485通讯主要是有线抄表&#xff0c;电表485通讯抄表软件选用485线传送数据&#xff0c;适宜集中化安装电表&#xff0c;下列给您具体说说电表485通讯抄表原理、应用领域等。 电表485通讯抄表原理 RS485抄表适用电表集中化安装场合&#xff0c;为节省RS485通讯线成本&…

VR渲染之Stereo Rendering解析

VR渲染的独特和最明显的方面之一是需要生成两个视图&#xff0c;左右眼睛各一个。我们需要这两个视图来为观众创建立体3D效果。 Multi Camera 传统上&#xff0c;VR应用程序必须绘制两次几何体--一次是左眼&#xff0c;一次是右眼。这基本上使非VR应用程序所需的处理翻了一番。…

揭秘百度智能测试在测试定位领域实践

作者 | intelligents 前几篇&#xff0c;分别介绍了测试活动测试输入、测试执行、测试分析、测试定位和测试评估五个步骤中测试输入、执行、分析、评估的智能化研究和实践&#xff0c;本章节重点介绍测试定位环节的智能化实践。 测试定位的主要作用是在构建失败或问题发生后&…

傻白探索Chiplet,国内外研究现状(六)

目录 一、概述 二、国外Chiplet历史与现状 2.1 AMD 2.1.1 EPYC&#xff08;Naples&#xff09; 2.1.2 EPYC&#xff08;Rome&#xff09; 2.1.3 EPYC&#xff08;Milan-X &#xff09; 2.1.4 Ryzen&#xff08;Matisse&#xff09; 2.2 苹果 2.3 Intel 2.3.1 Alter…

【大数据技术】Spark+Flume+Kafka实现商品实时交易数据统计分析实战(附源码)

需要源码请点赞关注收藏后评论区留言私信~~~ Flume、Kafka区别和侧重点 1&#xff09;Kafka 是一个非常通用的系统&#xff0c;你可以有许多生产者和消费者共享多个主题Topics。相比之下&#xff0c;Flume是一个专用工具被设计为旨在往HDFS&#xff0c;HBase等发送数据。它对H…

2022年我国江蓠行业现状:养殖面积、产量不断增长 进口量仍大于出口

根据观研报告网发布的《中国江蓠市场现状深度研究与发展前景预测报告&#xff08;2022-2029年&#xff09;》显示&#xff0c;江蓠属于“海藻”产业&#xff0c;为暖水性藻类&#xff0c;我国俗称 “龙须菜”、 “海菜”、 “蚝菜”。藻体紫褐色或紫黄色、绿色。 江蓠在热带、 …

Opencv(C++)笔记--霍夫变换检测直线、霍夫变换检测圆

目录 1--原理 2--Opencv API 3--实例代码 4--霍夫变换检测圆 1--原理 具体原理可参考 博客1 和 视频讲解1&#xff1b; 霍夫变换检测直线的核心思想是&#xff1a;在笛卡尔坐标系下&#xff0c;一条直线&#xff08;两个点&#xff08;x1, y1&#xff09;和&#xff08;x2,…

行业权威来揭秘,商用PC为什么首选12代酷睿

第12代酷睿处理器可以提供更卓越的性能&#xff0c;凭借架构先进性让商用台式机和笔记本电脑为用户带来更好的体验&#xff0c;帮助企业和员工效率倍增。 作者|九月 来源| PConline 想要让办公效率进一步提升&#xff0c;一台强大的PC设备是必不可少的生产力和内容创作工…

有什么适合零基础的人做的副业兼职

互联网上有很多套路。这是不可预防的。只要你敢贪婪&#xff0c;你就会陷入别人设计的陷阱。在业余时间做兼职应该是很多人的梦想&#xff0c;因为他们可以在有限的时间内赚更多的钱。很多人不知道的是&#xff0c;其实我们赚钱的渠道很多:比如网上发文章.短视频直播.我们媒体、…

基于SpringBoot+Mybatis框架的私人影院预约系统(附源码,包含数据库文件)

基于SpringBootMybatis框架的私人影院预约系统&#xff0c;附源码&#xff0c;包含数据库文件。 非常完整的一个项目&#xff0c;希望能对大家有帮助哈。 本系统的完整源码以及数据库文件都在文章结尾处&#xff0c;大家自行获取即可。 项目简介 该项目设计了基于SpringBoo…

Spring MVC—Spring MVC概述

文章目录Java web的发展历史一.Model I和Model II1.Model I开发模式2.Model II开发模式二. MVC模式SpringMVC 的工作原理和流程springmvc 的拦截器Spring和SpringMVC的区别————————————————————————————————Java web的发展历史 一.Model I和M…

VS Code debug调试时无法查看变量内容【已解决】

问题场景&#xff1a;新换成的vscode编译软件&#xff0c;但是在debug调试时发现与QtCreator不同&#xff0c;无法直接查看变量&#xff0c;显示的都是地址或其他。 比如&#xff1a;QString或QStringList无法查看具体的内容&#xff0c;正常是这样显示的&#xff0c;反正我不…

Linux神器——vim

目录 一、vim基本概念 二、vim基本操作 三、vim正常模式命令集 四、vim末行模式命令集 五、vim操作总结 六、vim界面配置 vi/vim的区别简单点来说&#xff0c;它们都是多模式编辑器&#xff0c;不同的是vim是vi的升级版本&#xff0c;它不仅兼容vi的所有指令&#xff0c;而…

上班15年后,普通程序员能实现财富自由吗?

对于职业生涯还没有开挂的普通程序员来说&#xff0c;有可能实现财务自由吗&#xff1f; 先来说下财务自由的最低标准 北上广深&#xff1a;身价3000万&#xff0c;含房产1000万、现金2000万。 杭州、南京、成都等二线城市&#xff1a;身价1500万&#xff0c;含500万房产、现…

集成底座双K8S集群扩展升级方案

集成底座方案是应用于企业信息化建设的集成整合阶段&#xff0c;通过建立统一、标准、柔性、可复用、可扩展的IT架构&#xff0c;解决企业信息化建设过程中缺乏整体规划、集成整合难度大、安全管控不到位等问题&#xff0c;强化企业信息化的架构建设、集成整合、数据治理、安全…

某鱼兼职并不是那么好做,钱也不是漫天要价

文章目录一、背景二、雇主的期望2.1、jinja2代码三、题主的期望3.1、删除功能3.2、前端体现3.3、留言列表实现降序3.4、效果显示四、总结一、背景 上周某鱼推送过来的单子多到题主应接不暇&#xff0c;不得已拒绝了几单&#xff0c;但是接下来的单子呢又不那么顺利&#xff0c;…

提速3.7倍!何恺明团队再发新作,更快更高效的FLIP模型:通过Masking扩展语言-图像预训练(附论文原文下载)

原创/文 BFT机器人 研究论文地址&#xff1a;https://arxiv.org/abs/2212.00794 计算机视觉和深度学习领域大神何恺明携团队再发新作&#xff01;论文围绕近来火热的CLIP&#xff08;Contrastive Language-Image Pre-Training&#xff09;模型展开研究&#xff0c;并提出了一种…

Python怎么进行时区的转换

pytz 是一个用于处理时区的 Python 库,它为 Python 提供了对时区的支持。 它提供了大量的时区信息&#xff0c;包括时区名称、偏移量、是否使用夏令时等。你可以使用 pytz 库来处理本地时间、UTC 时间和其他时区之间的转换。 它提供了许多函数来帮助你处理时区相关的信息。 …

C++之多态(中篇)(最全总结)

这里接上面C之多态&#xff08;上篇&#xff09; 本篇目录4.多态的原理4.2 多态的原理4.3 C 11 override和final4.4 重载、重写&#xff08;覆盖&#xff09;、隐藏&#xff08;重定义&#xff09;的对比 &#xff08;函数之间的关系&#xff09;5.抽象类5.1概念5.2接口继承和实…

三、基于kubeadm安装kubernetes1.25集群第二篇

在上一篇中我们已经安装kubernetes要求做了服务器初始化&#xff0c;看这篇之前&#xff0c;建议先看下上篇&#xff1a;https://blog.csdn.net/u011837804/article/details/128350651 那我们正式开始kubernetes1.26集群安装 1、每台机器安装docker20.10.22 docker的安装细节…