实例讲解,一文弄懂workqueue和waitqueue

本期主题:
讲清workqueue和waitqueu:

  1. 从中断讲起
  2. waitqueue是什么
  3. workqueue
  4. 总结

往期链接:

  • linux设备驱动中的并发
  • linux设备驱动中的编译乱序和执行乱序
  • linux设备驱动之内核模块
  • linux字符驱动
  • linux字符驱动之ioctl部分
  • linux字符驱动之read、write部分
  • linux驱动调试之Debugfs
  • Linux下如何操作寄存器(用户空间、内核空间方法讲解)
  • petalinux的module编译

目录

  • 1.从中断讲起
  • 2.workqueue
    • Linux上workqueue例子
      • workqueue API
      • example
  • 3.waitqueue
    • Linux上waitqueue例子
      • waitqueue API
    • example
  • 4.总结


1.从中断讲起

从操作系统角度而言,实际上有两种中断:

  1. 硬件中断(hardware IRQ),由系统自身和外设产生的中断,例如在ARM侧就是GIC中断;
  2. 软中断(software IRQ),用于实现延时执行操作(这是重点,后面会有解释);

我们来分析一下 中断处理程序 的需求

需求:

  1. 中断处理程序需要尽可能简短,这样能够快速处理中断;
  2. 允许在处理A中断时,被B中断打断然后去处理B的事情,允许中断的嵌套;

由于有快速、简短这样的需求,所以操作系统设计出了这样的机制:

  1. 关键操作(可以理解为清除一些中断状态位的操作),必须在中断发生后立刻去做;
  2. 非关键操作也应尽快执行,但允许启用中断;
  3. 可延期操作不是特别重要,不必在中断处理程序中运行,可以延迟这些操作,在时间充裕时进行;

软中断就是这样的一个作用,可以使操作延时去做

2.workqueue

workqueue顾名思义就是工作队列,他有几个特点:

  1. 处理不那么紧急的任务,我们常说的Linux中断处理的底半部就可以使用workqueue来做;
  2. 函数执行环境的切换,从中断环境切到了线程环境
  3. 总而言之,workqueue适用于异步执行的场景;

workqueue就相当于在worklist(工作链表)上挂上了一个个的work item(工作节点),每次都从中取出这些item来运行
在这里插入图片描述

Linux上workqueue例子

workqueue API

讲解以下几个API:

  1. workqueue的结构体定义
  2. work_struct的结构体定义(work item)
  3. delay_work接口

1. workqueue定义:

//kernel/workqueue.c
其中的pwqs是 pool workqueues,代表着这个workqueue所管理的pool,pool在这里的意思理解是一个池子,存放着当前wq的信息
在这里插入图片描述

2. workstruct定义

其中的func就是work_item的callback函数,entry就是添加到前面的workqueue_strcut的pwqs list中,然后遍历
在这里插入图片描述

3. queue_delayed_work

在这里插入图片描述

在这里插入图片描述

example

看一个例子,我们写一个ko,实现的功能如下:

  1. 在init的时候创建workqueue,并且添加delay work,delay时间设置为1s
  2. 1s之后,调用我们的work handler,做一个异步处理的典型场景
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/workqueue.h>

static void mykmod_work_handler(struct work_struct *w);

static struct workqueue_struct *wq = 0;
static DECLARE_DELAYED_WORK(mykmod_work, mykmod_work_handler);
static unsigned long onesec;

static void
mykmod_work_handler(struct work_struct *w)
{
        pr_info("mykmod work %u jiffies\n", (unsigned)onesec);
}


static int mykmod_init(void)
{
        onesec = msecs_to_jiffies(1000);
        pr_info("mykmod loaded %u jiffies\n", (unsigned)onesec);

        wq = create_singlethread_workqueue("mykmod");
        if (!wq)
                pr_err("wq init failed!\n");

        queue_delayed_work(wq, &mykmod_work, onesec);

        return 0;
}

static void mykmod_exit(void)
{
        if (wq) {
                cancel_delayed_work_sync(&mykmod_work);
                destroy_workqueue(wq);
        }
        pr_info("mykmod exit\n");
}

module_init(mykmod_init);
module_exit(mykmod_exit);

MODULE_DESCRIPTION("mykmod");
MODULE_LICENSE("GPL");

测试结果:
在这里插入图片描述

3.waitqueue

等待队列用于在进程中等待某一特定事件发生,无须频繁轮询。进程在等待期间休眠,事件发生时,内核唤醒进程。总结一下有几个特点:

  1. 进程等不到时会休眠;
  2. 事件发生时,内核会把进程唤醒;

Linux上waitqueue例子

waitqueue API

介绍waitqueue结构体以及下面三种API:

1.waitqueue结构体
2. 初始化等待队列,DECLARE_WAIT_QUEUE_HEAD
3. 将进程加入等待队列,wait_event
4. 唤醒等待队列中的进程,wakeup

1. waitqueue 结构体
在这里插入图片描述

  1. waitqueue head只有一个自旋锁和双向链表;
  2. wait_queue_entry是对一个等待任务的抽象,每个等待任务都会抽象成一个wait_queue_t,并且挂载到wait_queue_head_t上;

2. 初始化等待队列
初始化等待队列,实际上就是waitqueue_head的初始化,可以使用静态初始化也可使用动态初始化;

  1. 静态初始化: DECLARE_WAITQUEUE
  2. 动态初始化:init_waitqueue_head
    在这里插入图片描述

3. 将进程加入等待队列

wait_event(wq_head, condition)
在这里插入图片描述
等待事件接口,sleep直至conditon满足

其中,__wait_event的代码如下:
在这里插入图片描述

4. wakeup
在这里插入图片描述

通过遍历wq_head来找到entry,并且调用entry的func;

example

例子讲解:

  1. 创建一个静态等待队列,并在Moudule init的时候建立一个内核进程;
  2. 注册好proc_fs,上层通过Cat读取/proc文件时,会将flag置1,并wakeup等待进程;
  3. 在rmmod 模块时,会将flag置2,并wakup等待进程,此时内核进程退出;

下面是代码以及测试结果:

#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
#include <linux/wait.h>
#include <linux/kthread.h>
static int read_count = 0;
static struct task_struct *wait_thread;
// Initializing waitqueue statically
DECLARE_WAIT_QUEUE_HEAD(test_waitqueue);
static int wait_queue_flag = 0;
static int my_waitqueue_show(struct seq_file *m, void *v)
{
        printk(KERN_ALERT "Read function\n");
        seq_printf(m, "read_count = %d\n", read_count);
        wait_queue_flag = 1;
        wake_up_interruptible(&test_waitqueue); // wake up only one process from wait queue
        return 0;
}
static int my_waitqueue_open(struct inode *inode, struct file *filp)
{
        return single_open(filp, my_waitqueue_show, NULL);
}
static struct proc_fs test_wait_queue_fops = {
        .proc_open           = my_waitqueue_open,
        .proc_read           = seq_read,
        .proc_lseek         = seq_lseek,
        .proc_release        = single_release,
};
static int wait_function(void *unused)
{
        while(1) {
                printk(KERN_ALERT "Waiting For Event...\n");
                // sleep until wait_queue_flag != 0
                wait_event_interruptible(test_waitqueue, wait_queue_flag != 0);
                if (wait_queue_flag == 2) {
                        printk(KERN_ALERT "Event Came From Exit Function\n");
                        return 0;
                }
                printk(KERN_ALERT "Event Came From Read Function - %d\n", ++read_count);
                wait_queue_flag = 0;
        }
        return 0;
}
static int mywaitqueue_init(void)
{
        struct proc_dir_entry *pe;
        printk(KERN_ALERT "[Hello] mywaitqueue \n");
        pe = proc_create("test_wait_queue", 0644, NULL, &test_wait_queue_fops);
        if (!pe)
                return -ENOMEM;
        // Create the kernel thread with name "MyWaitThread"
        wait_thread = kthread_create(wait_function, NULL, "MyWaitThread");
        if (wait_thread) {
                printk(KERN_ALERT "Thread created successfully\n");
                wake_up_process(wait_thread);
        } else {
                printk(KERN_ALERT "Thread creation failed\n");
        }
        return 0;
}
static void mywaitqueue_exit(void)
{
        wait_queue_flag = 2;
        wake_up_interruptible(&test_waitqueue);
        printk(KERN_ALERT "[Goodbye] mywaitqueue\n");
        remove_proc_entry("test_wait_queue", NULL);
}
module_init(mywaitqueue_init);
module_exit(mywaitqueue_exit);
MODULE_LICENSE("GPL");

测试结果:
进行了3次cat读取,最后rmmod才真正退出
在这里插入图片描述

4.总结

waitqueueworkqueue 是实时操作系统中用于线程间通信和任务调度的两种机制,它们有一些区别和不同的应用场景。

waitqueue 是一种线程等待队列,用于实现线程间的同步和通信。它允许线程等待某个条件的发生,并在条件满足时被唤醒继续执行。通过将线程放入等待队列中,可以避免线程在条件不满足时的忙等待,提高系统的效率和资源利用率。waitqueue 主要用于实现线程间的同步和事件通知,例如一个线程等待某个资源的可用性或某个事件的发生。

workqueue 是一种任务队列,用于调度和执行后台任务。它可以在后台异步执行一些耗时的操作,而不阻塞当前线程的执行。workqueue 将任务添加到队列中,并由系统自动调度和执行。相比于直接在当前线程中执行任务,使用 workqueue 可以将任务的执行延迟到后台,提高系统的响应性和并发性。workqueue 主要用于处理一些耗时的、非实时性的任务,例如文件系统操作、网络请求、数据处理等。

下面是 waitqueueworkqueue 的一些主要差异:

  1. 功能目的:waitqueue 用于线程间的同步和通信,而 workqueue 用于后台任务的调度和执行。

  2. 调度方式:waitqueue 中的线程需要显式地被唤醒,可以通过条件的满足或其他线程的通知来触发唤醒操作;而 workqueue 中的任务由系统自动调度,无需显式触发。

  3. 使用场景:waitqueue 主要用于实现同步和事件通知的场景,例如等待某个资源的可用性或某个事件的发生;而 workqueue 主要用于执行后台任务,例如文件系统操作、网络请求、数据处理等。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

分布式存储Ceph的部署及应用(创建MDS、RBD、RGW 接口)

系列文章目录 文章目录 系列文章目录一、1.存储基础2. 单机存储的问题3. 分布式存储&#xff08;软件定义的存储 SDS&#xff09; 二 Ceph1.Ceph 简介2. Ceph 数据的存储过程 总结 一、 1.存储基础 1.1 单机存储设备 ●DAS&#xff08;直接附加存储&#xff0c;是直接接到计算…

SAX解析XML返回对应格式的Map对象

前言 最近有一个解析大型xml的需求&#xff0c;xml大小7M&#xff0c;其中xml结构非常复杂&#xff0c;元素各种嵌套 不乏有元素下对象&#xff0c;元素下集合&#xff0c;集合下对象&#xff0c;集合下集合&#xff0c;兄弟不同元素节点&#xff0c;元素下对象下集合&#xff…

HDFS读写流程

读数据流程 客户端向NameNode请求文件的位置&#xff1a;客户端想要访问一个文件时&#xff0c;会向NameNode发送一个请求&#xff0c;要求获取该文件在HDFS上的位置信息。 NameNode将位置信息返回给客户端&#xff1a;NameNode接收到客户端的请求后&#xff0c;会返回该文件所…

设计模式—访问者模式

需求&#xff1a;店铺采购了一批水果&#xff08;苹果及橘子&#xff09;&#xff0c;现在市场监督局来店里检查过期的水果。 public class Fruit {private String name;private Date pickDate;public Fruit(String name, Date pickDate) {this.name name;this.pickDate pic…

javaWeb之cookiesession

1 回顾 1.1 response对象 一次响应封装对象&#xff0c;由服务器创建。使用response对象将服务器需要的数据发送给浏览器。 将数据存放response对象中&#xff0c;tomcat从response对象获得数据&#xff0c;根据数据组织http响应&#xff0c;最后将http响应内容发送给浏览器&…

selenium自动化教程及使用java来爬取数据

目录 一、介绍二、下载浏览器驱动1.获取要下载的驱动版本号2.下载驱动 三、Maven如下四、简单使用五、定位器1.定位器2.说明(1) class name 定位器(2) css selector 定位器(3) id 定位器(4) name 定位器(5) link text 定位器(6) partial link text 定位器(7) tag 定位器(8) xpa…

第8讲:$.ajax方法使用详解

jQuery对象上面定义了Ajax方法&#xff08;$.ajax()&#xff09;&#xff0c;用来处理Ajax操作。调用该方法后&#xff0c;浏览器就会向服务器发出一个HTTP请求。ajax方法有很多属性&#xff0c;但并非每次调用都要使用所有属性&#xff0c;本讲详细介绍了每个属性的作用&#…

windows搭建vue开发环境

参考博客&#xff1a;最详细的vue安装教程_一只野生程序媛的博客-CSDN博客 Vue安装环境最全教程&#xff0c;傻瓜式安装_浪漫主义码农的博客-CSDN博客 1、安装nodejs&#xff0c;从下面官网下载版本&#xff0c;对应安装就行了&#xff1a; Node.js 中文网 2、安装好后&…

Linux 多路转接 —— poll

目录 传统艺能&#x1f60e;poll&#x1f923;struct pollfd&#x1f923; poll 服务器&#x1f618;PollServer类&#x1f601;运行服务器&#x1f612;事件处理&#x1f601; 服务器测试&#x1f602; 传统艺能&#x1f60e; 小编是双非本科大二菜鸟不赘述&#xff0c;欢迎米…

0基础学习地平线QAT量化感知训练

文章目录 1. 背景2. 基础理论知识3. 文件准备与程序运行4. 代码详解4.1 导入必要依赖4.2 主函数4.3 构建fx模式所需要的float_model4.4 不同阶段模型的获取4.5 定义常规模型训练与验证的函数4.6 float与qat训练代码解读——float_model/qat_model4.7 模型校准部分的代码解读——…

七、docker-compose方式运行Jenkins,更新Jenkins版本,添加npm node环境

docker-compose方式运行Jenkins&#xff0c;更新Jenkins版本&#xff0c;添加npm node环境 一、docker-compose方式安装运行Jenkins 中发现Jenkins版本有点老&#xff0c;没有node环境&#xff0c;本节来说下更新jenkins 及添加构建前端的node环境。 1. 准备好docker-compose…

三种方法将Word文档转换为PDF文件格式

如何将Word文档转换为PDF文件格式呢&#xff1f;大家在传输文件时&#xff0c;很多人喜欢使用PDF文件格式&#xff0c;因为它非常稳定&#xff0c;不会出现格式混乱的问题。但有些人可能不知道如何进行转换&#xff0c;今天我将介绍三种转换方法&#xff0c;让我们一起来学习一…

mysql 删表引出的问题

背景 将测试环境的表同步到另外一个数据库服务器中&#xff0c;但有些表里面数据巨大&#xff0c;&#xff08;其实不同步该表的数据就行&#xff0c;当时没想太多&#xff09;&#xff0c;几千万的数据&#xff01;&#xff01; 步骤 1. 既然已经把数据同步过来的话&#x…

环境配置 | Git的安装及配置[图文详情]

Git是一个开源的分布式版本控制系统&#xff0c;可以有效、高速地处理从小到大的项目版本管理。下面介绍了基础概念及详细的用图文形式介绍一下git安装过程. 目录 1.Git基础概念 2.Git的下载及安装 3.常见的git命令 Git高级技巧 Git与团队协作 1.Git基础概念 仓库&#…

认识异常

目录 异常的概念与体系结构 异常的概念 异常的体系结构 异常的分类 异常的处理 防御式编程 1. LBYL: 2. EAFP: 异常的抛出 异常的捕获 异常声明throws try-catch捕获并处理 关于异常的处理方式 finally 异常的处理流程 自定义异常类 异常的概念与体系结构 异常…

基于SpringBoot+kaptcha的验证码生成

教程 1.添加 Kaptcha 依赖 在 pom.xml 文件中添加 Kaptcha 依赖&#xff1a; <dependency><groupId>com.github.penggle</groupId><artifactId>kaptcha</artifactId><version>2.3.2</version> </dependency> <!--或者 都…

平凯星辰重磅支持 2023 开放原子全球开源峰会,开源数据库分论坛成功召开

2023 年 6 月 11 日至 13 日&#xff0c;以“开源赋能&#xff0c;普惠未来”为主题的 2023 开放原子全球开源峰会开幕式暨高峰论坛在北京成功举办。企业级开源分布式数据库厂商平凯星辰联合创始人兼 CTO 黄东旭受邀出席峰会参与开源论道圆桌&#xff0c;担任开源数据库分论坛出…

第一章 数据可视化简介(复习)

第一章 数据可视化简介 什么是可视化 定义&#xff1a;通过可视表达增强人们完成某些 任务的效率 The American Heritage Dictionary&#xff1a; The act or process of interpreting in visual terms or of putting into visible form&#xff08;用可视形式进行解释的 动作…

VUE L ∠脚手架 配置代理 ⑩⑧

目录 文章有误请指正&#xff0c;如果觉得对你有用&#xff0c;请点三连一波&#xff0c;蟹蟹支持✨ V u e j s Vuejs Vuejs初识 V u e C L I VueCLI VueCLI C L I CLI CLI V u e Vue Vue配置代理 C L I CLI CLI配置方法一 C L I CLI CLI配置方法二 C L I CLI CLI V u …

EL标签-给JSP减负

https://blog.csdn.net/weixin_42259823/article/details/85945149 安装使用 1. 通过命令行创建maven项目 2. 安装jstl包 <dependency><groupId>jstl</groupId><artifactId>jstl</artifactId><version>1.2</version> </depen…