itop-3568开发板驱动学习笔记(19)内核工作队列

news2024/11/15 13:32:04

《【北京迅为】itop-3568开发板驱动开发指南.pdf》 学习笔记

文章目录

  • 工作队列简介
  • 共享工作队列
    • 工作结构体
    • 初始化 work_struct
    • 调度工作队列函数
    • 共享工作队列实验
  • 自定义工作队列
    • 创建工作队列函数
    • 调度和取消调度工作队列
    • 刷新工作队列函数
    • 删除工作队列函数
  • 内核延时工作队列
    • 延时工作结构体
    • 初始化延时工作函数
    • 调度延时工作函数
    • 取消调度延时工作函数
  • 工作队列传参
  • 并发管理工作队列
    • 创建一个并发管理工作队列

工作队列简介

工作队列是实现中断下半部分的机制之一,是一种将工作推后执行的形式,工作队列和同为中断下半部分的 tasklet 的区别在于 tasklet 不能休眠(且以原子模式执行),而工作队列可以休眠(不必原子执行)。
内核工作队列分为共享工作队列和自定义工作队列两种。

共享工作队列

共享工作队列是内核提供的默认工作队列,共享意味着不能长时间独占该队列,既不能长时间休眠,且我们的任务可能需要等待更长的时间才能被执行。

工作结构体

#include <linux/workqueue.h>

struct work_struct
{
	atomic_long_t data;
	struct list_head entry;
	work_func_t func;
};

该结构体最重要的成员为 func 函数指针,该函数指针原型为:

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

初始化 work_struct

INIT_WORK()DECLARE_WORK() 用来初始化一个 work_struct 结构体,前者为动态初始化,后者为静态初始化,函数定义为:

INIT_WORK(_work, _func);
DECLARE_WORK(n, f);

它们的第一个参数为 work_struct 结构体指针,第二个参数为工作函数指针。

调度工作队列函数

函数原型:

static inline bool schedule_work(struct work_struct *work);

如果想在中断下文执行工作函数,则需要在中断处理函数中调用该函数。

共享工作队列实验

实验代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

int irq;
struct work_struct my_work; 

//工作函数
void my_work_func(struct work_struct *work)
{
	printk("my work func.\n");
	msleep(1000);
	printk("msleep finish.\n");
}

//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
	printk("my interrupt handler.\n");	
	
	// 调度工作队列
	schedule_work(&my_work);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
	int ret = 0;
	
	// 获取中断号
	irq = gpio_to_irq(101);
	printk("irq is %d\n", irq);

	// 申请中断
	ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
	if(ret < 0)
	{
		printk("request irq error.\n");
		return 0;
	}
	
	// 初始化工作队列
	INIT_WORK(&my_work, my_work_func);

	return 0;	
}

static void interrupt_irq_exit(void)
{
	printk("interrupt irq exit.\n");

	// 注销中断
	free_irq(irq, NULL);
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");

实验结果

实验效果和 tasklet 类似,只是工作函数可以支持延时操作。

在这里插入图片描述

自定义工作队列

共享工作队列是内核提供的一个公共的工作队列,我们也可以自己创建工作队列。

创建工作队列函数

create_workqueue() 和 create_singlethread_workqueue() 可以用来创建自定义工作队列,它们的定义如下(都是宏函数):

create_workqueue(name);
create_singlethread_workqueue(name);

create_workqueue() 可以给每个 CPU 都创建一个工作队列,name 为工作队列名,创建成功返回 workqueue_struct 结构体指针,失败返回 NULL。

create_singlethread_workqueue() 只给一个 CPU 创建工作队列。

调度和取消调度工作队列

queue_work_on() 用来调度自定义工作队列,cancel_work_sync() 用来取消已经调度的工作,并且会等待其完成再返回。

bool queue_work_on(int cpu, struct workqueue_struct *wq, struct work_struct *work);
bool cancel_work_sync(struct work_struct *work);

刷新工作队列函数

告知内核尽快处理工作队列的工作。

void flush_workqueue(struct workqueue_struct *wq);

删除工作队列函数

删除一个自定义的工作队列。

void destroy_workqueue(struct workqueue_struct *wq);

实验代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

int irq;
struct work_struct my_work;
struct workqueue_struct *my_workqueue; 

//工作函数
void my_work_func(struct work_struct *work)
{
	printk("my work func.\n");
	msleep(1000);
	printk("msleep finish.\n");
}

//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
	printk("my interrupt handler.\n");	
	
	// 调度自定义工作队列
	queue_work(my_workqueue, &my_work);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
	int ret = 0;
	
	// 获取中断号
	irq = gpio_to_irq(101);
	printk("irq is %d\n", irq);

	// 申请中断
	ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
	if(ret < 0)
	{
		printk("request irq error.\n");
		return 0;
	}
	
	// 创建自定义工作队列
	my_workqueue = create_workqueue("my_workqueue");
	// 初始化工作队列
	INIT_WORK(&my_work, my_work_func);

	return 0;	
}

static void interrupt_irq_exit(void)
{
	printk("interrupt irq exit.\n");

	// 注销中断
	free_irq(irq, NULL);
	
	// 取消自定义工作队列调度
	cancel_work_sync(&my_work);
	
	// 刷新工作队列
	flush_workqueue(my_workqueue);
	
	// 删除工作队列
	destroy_workqueue(my_workqueue);
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");

实验结果

实验效果和共享工作队列一致。

在这里插入图片描述

内核延时工作队列

延时工作结构体

struct delayed_work
{
	struct work_struct work;
	struct timer_list timer;
}

work 成员为之前提到的工作结构体,延时工作结构体只比工作结构体多了内核定时器。

初始化延时工作函数

初始化分为静态初始化和动态初始化,DECLARE_DELAYED_WORK(n, f) 用来静态初始化延时工作结构体,INIT_DELAYED_WORK(_work, _func) 用来动态初始化延时工作。

调度延时工作函数

schedule_delayed_work() 的作用是调度共享工作队列上的延时工作,queue_delayed_work() 则用来调度自定义工作队列上的延时工作。

static inline bool schedule_delayed_work(struct delayed_work *dwork, unsinged long delay);
static inline bool queue_delayed_work(struct workqueue_struct *wq, struct delayed_work *dwork, unsigned long delay);

dwork 为延时工作结构体变量,delay 为要延时的时间,单位为节拍,queue_delayed_work() 的第一个参数为自定义工作队列结构体指针。

取消调度延时工作函数

用来取消已经调度的延时工作。

bool cancel_delayed_work_sync(struct delayed_work *dwork);

实验代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

int irq;
struct delayed_work my_work; 
struct workqueue_struct *my_workqueue;


//工作函数
void my_work_func(struct work_struct *work)
{
	printk("my work func.\n");
	msleep(1000);
	printk("msleep finish.\n");
} 

//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
	printk("my interrupt handler.\n");	
	
	// 调度延时自定义工作队列,延时 3 秒
	queue_delayed_work(my_workqueue, &my_work, 3 * HZ);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
	int ret = 0;
	
	// 获取中断号
	irq = gpio_to_irq(101);
	printk("irq is %d\n", irq);

	// 申请中断
	ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
	if(ret < 0)
	{
		printk("request irq error.\n");
		return 0;
	}
	
	// 创建自定义工作队列
	my_workqueue = create_workqueue("my_workqueue");
	
	// 初始化工作队列
	INIT_DELAYED_WORK(&my_work, my_work_func);

	return 0;	
}

static void interrupt_irq_exit(void)
{
	printk("interrupt irq exit.\n");

	// 注销中断
	free_irq(irq, NULL);
	
	// 取消延时自定义工作队列
	cancel_delayed_work_sync(&my_work);
	
	// 刷新工作队列
	flush_workqueue(my_workqueue);
	
	// 删除工作队列
	destroy_workqueue(my_workqueue);
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");

实验结果

相较前面的实验,延时工作工作函数会在中断触发后,延后一段时间再执行。

在这里插入图片描述

工作队列传参

工作处理函数的参数为 work_struct,即工作函数运行时可以使用对应的工作结构体变量,如果我们定义一个结构体,并将 work_struct 作为它的成员,那么就能在工作函数中访问我们自定义的结构体变量了。

具体实现需要用到 container_of() 宏函数(函数功能:从结构体某个成员的首地址获取整个结构体的首地址)

实验代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

int irq;

//自定义数据结构体
struct work_data
{
	struct work_struct my_work;
	int a;
	int b;
};

struct work_data my_work_data;
struct workqueue_struct *my_workqueue;


//工作函数
void my_work_func(struct work_struct *work)
{	
	struct work_data *pdata;
	pdata = container_of(work, struct work_data, my_work);
	
	//printk("my work func.\n");
	printk("data a is %d.\n", pdata->a);
	printk("data b is %d.\n", pdata->b);
} 

//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
	printk("my interrupt handler.\n");	
	
	// 调度自定义工作队列
	queue_work(my_workqueue, &my_work_data.my_work);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
	int ret = 0;
	
	// 获取中断号
	irq = gpio_to_irq(101);
	printk("irq is %d\n", irq);

	// 申请中断
	ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
	if(ret < 0)
	{
		printk("request irq error.\n");
		return 0;
	}
	
	// 创建自定义工作队列
	my_workqueue = create_workqueue("my_workqueue");
	
	// 初始化工作队列
	INIT_WORK(&my_work_data.my_work, my_work_func);

	// 自定义数据结构体成员初始化
	my_work_data.a = 5;
	my_work_data.b = 6;
	
	return 0;	
}

static void interrupt_irq_exit(void)
{
	printk("interrupt irq exit.\n");

	// 注销中断
	free_irq(irq, NULL);
	
	// 取消自定义工作队列
	cancel_work_sync(&my_work_data.my_work);
	
	// 刷新工作队列
	flush_workqueue(my_workqueue);
	
	// 删除工作队列
	destroy_workqueue(my_workqueue);
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");

实验结果

成功实现工作函数的传参:

在这里插入图片描述

并发管理工作队列

并发管理工作队列(CMWQ)设计的目的为:试图保持最小的资源消耗,但要保证足够的并发性。CMWQ使用最小的资源来发挥它的全部能力。

创建一个并发管理工作队列

alloc_workqueue(fmt, flags, max_active);

fmt 为要创建工作队列的名称,max_active 为线程池里最大的线程数量(默认填 0),flags 可取值包括:

flags简介
WQ_UNBOUNDungound 队列不绑定指定 CPU,不会参与并发管理(不会出现并发冲突问题)
WQ_FREEZABLE在该队列上工作的工作不会被执行
WQ_MEM_RECLAIM所有有可能运行在内存回收流程中的工作队列都需要设置该标记
WQ_HIGHPRIhighpri 队列上的工作会被指定的 CPU 上的线程池来处理
WQ_CPU_INTENSIVECPU 密集型工作队列

本笔记只用到了 WQ_UNBOUND 标志,之前介绍的工作队列,每个工作队列只运行在特定的 CPU 上,如果多个工作队列需要并发运行时,创建 unbound 队列可以使内核线程在多个处理器之间迁移。

实验代码

#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/gpio.h>
#include <linux/workqueue.h>
#include <linux/delay.h>

int irq;
struct work_struct my_work;
struct workqueue_struct *my_workqueue; 

//工作函数
void my_work_func(struct work_struct *work)
{
	printk("my work func.\n");
	msleep(1000);
	printk("msleep finish.\n");
}

//中断服务函数
irqreturn_t my_interrupt(int irq, void *args)
{
	printk("my interrupt handler.\n");	
	
	// 调度自定义工作队列
	queue_work(my_workqueue, &my_work);
	
	return IRQ_RETVAL(IRQ_HANDLED);
}

static int interrupt_irq_init(void)
{
	int ret = 0;
	
	// 获取中断号
	irq = gpio_to_irq(101);
	printk("irq is %d\n", irq);

	// 申请中断
	ret = request_irq(irq, my_interrupt, IRQF_TRIGGER_RISING, "inttrupt_test", NULL);
	if(ret < 0)
	{
		printk("request irq error.\n");
		return 0;
	}
	
	// 创建并发工作队列
	my_workqueue = alloc_workqueue("my_workqueue", WQ_UNBOUND, 0);
	
	// 初始化工作队列
	INIT_WORK(&my_work, my_work_func);

	return 0;	
}

static void interrupt_irq_exit(void)
{
	printk("interrupt irq exit.\n");

	// 注销中断
	free_irq(irq, NULL);
	
	// 取消自定义任务队列调度
	cancel_work_sync(&my_work);
	
	// 刷新工作队列
	flush_workqueue(my_workqueue);
	
	// 删除工作队列
	destroy_workqueue(my_workqueue);
}

module_init(interrupt_irq_init);
module_exit(interrupt_irq_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xiaohui");

实验结果

并发管理工作队列测试效果和普通工作队列并没有什么区别(因为测试例程过于简单)

在这里插入图片描述

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

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

相关文章

成功上岸字节35K,技术4面+HR面,耗时20天,真是不容易

这次字节的面试&#xff0c;给我的感触很深&#xff0c;意识到基础的重要性。一共经历了五轮面试&#xff1a;技术4面&#xff0b;HR面。 下面看正文 本人自动专业毕业&#xff0c;压抑了五个多月&#xff0c;终于鼓起勇气&#xff0c;去字节面试&#xff0c;下面是我的面试过…

kali利用airgeddon破解WiFi (详细安装和使用教程)

目录 前言 一&#xff0c;软件&硬件环境 二&#xff0c;前期配置 Airgeddon安装和调试 #自带 #安装方法一 #安装方法二 #注意 网卡的配置 #打开服务 #加载网卡 三&#xff0c;运行操作 #检查 #主菜单 #打开监听模式 #查看周围可以攻击的网络 #截取…

vue - - - - - vue3全局配置挂载axios

vue3配置axios 1. 安装axios2. 配置拦截器3. vue.config.js代理配置4. 将axios全局挂载4. 文件内使用 1. 安装axios yarn add axios 2. 配置拦截器 创建文件 /src/utils/request.js "use strict";import Vue from "vue"; import axios from "axios&…

从现在起,请你不要用ChatGPT再做这4件事了

ChatGPT已经火爆了快半年了吧&#xff0c;紧接着国内也开始推出了各种仿制品&#xff0c;我甚至一度怀疑&#xff0c;如果人家没有推出ChatGPT&#xff0c;这些仿制品会不会出现。而很多人也嗨皮得不行&#xff0c;搭着梯子爬过高墙&#xff0c;用ChatGPT做各种觉得新鲜的事。但…

电脑可以开机但是无法进入到桌面怎么办?

电脑可以开机但是无法进入到桌面怎么办&#xff1f;有用户的电脑可以正确启动&#xff0c;但是电脑启动之后&#xff0c;却无法进入到系统桌面&#xff0c;而且卡在加载系统的页面中&#xff0c;或者是出现错误代码蓝屏了。这些情况其实都可以通过U盘来重装一个系统&#xff0c…

第七回:如何使用GirdView Widget

文章目录 概念介绍使用方法示例代码经验总结 我们在上一章回中介绍了Image Widget,本章回中将介绍 GirdView这种Widget&#xff0c;闲话休提&#xff0c;让我们一起Talk Flutter吧。 概念介绍 在Flutter中使用GirdView表示网格状的布局&#xff0c;类似日常办公中使用的Excel…

hashCode 如何计算?这一篇就够了!

介绍 hashCode 中文‘散列码’&#xff0c;存在的意义是加快查找速率&#xff0c;可以在常数时间内进行寻址操作。 存在意义 它被定义在 Object 中&#xff0c;而 Object 类是一切类的父类&#xff0c;所以所有的方法都具有这个方法。 Java 中 hashCode 计算方式如下&#x…

C2. Exam in BerSU (hard version)(思维 + 小数据范围)

Problem - C2 - Codeforces 简单版本和困难版本之间的唯一区别是约束。 如果你用Python写一个解决方案&#xff0c;那么最好用PyPy发送&#xff0c;以加快执行时间。 贝兰德州立大学的一场会议已经开始。许多学生正在参加考试。 波利格拉夫维奇要对N个学生进行考试。学生们将…

工业树莓派远程I/O控制套装—更高效、更灵活、更便捷

一、背景 在完整的生产过程中&#xff0c;许多传感器设备和执行设备不完全安装在同一位置&#xff0c;大多分散部署在各个生产环节中。如果采用本地控制的方式&#xff0c;就需要用到多个控制器&#xff0c;但是成本较高&#xff0c;且不利于管理&#xff0c;所以最理想的解决…

2.redis-持久化

01-Redis持久化 概述 Redis数据存储在缓存中&#xff0c;为了防止数据的意外丢失&#xff0c;确保数据安全性。所以&#xff0c;就有了redis持久化。 分类 RDB: Redis默认的持久化策略, 直接存储数据 AOF: 存储数据的操作过程. 02-RDB持久化之save指令 配置说明 # 设置rdb…

[pgrx开发postgresql数据库扩展]2.安装与开发环境的搭建

——前文再续&#xff0c;书接上一回。 前言 我上篇文章刚刚写完&#xff0c;pgx就全面改名为了 pgrx……&#xff0c;结果导致我都来不及把以前的文章改过来&#xff0c;所以以后遵循最新的命名方法。 pgrx的开发环境需求 pgrx目前仅支持在linux操作系统上进行开发&#xff…

Android 基于 Perfetto 抓取 trace

Perfetto 官方链接地址 https://github.com/google/perfetto/ 开启Android的trace跟踪服务 Perfetto 是基于 Android 的系统追踪服务&#xff0c; 这个配置在 Android11 之后是默认打开的&#xff0c;但是如果你是 Android 9 ( P ) 或者 10 ( Q ) &#xff0c;那么就需要手动设…

【备考2023年软考】选系统规划与管理师,还是信息系统项目管理师?

目录 一、系统规划与管理师介绍 二、信息系统项目管理师介绍 三、二者区别 四、适合什么人考 五、怎么备考 1.了解考试大纲 2.系统学习&#xff08;附带资料分享&#xff09; 3.多做题 4.总结复习 软考系统规划与管理师和信息系统项目管理师是软考中的两个比较热门的证…

CSS——js 动态改变原生 radio、switch 的选中样式

导航 1. radio1-1. 业务场景&#xff1a;1-2. 效果&#xff1a;1-3. 问题点&#xff1a;1-4. 解决方案&#xff1a;1-5. 代码&#xff1a;1-5-1. HTML1-5-2. JS1-5-3. html 内容排版的 css1-5-4. 实现 radio 效果的 css 2. switch2-1. 业务场景&#xff1a;2-2. 效果&#xff1…

Vue3+Typescript+Vitest单元测试环境+组件事件测试篇

上一节我们学会了组件测试的基础测试部分组件测试基础篇&#xff0c;这一节&#xff0c;我们学习一下深入测试组件的事件 在component中增加一个新的组件,名字就叫做Zmbutton2吧 import { defineComponent } from "vue";const ZmButton2 defineComponent({name: &…

《Spring MVC》 第一章 MVC模式

前言 MVC 模式&#xff0c;全称为 Model-View-Controller&#xff08;模型-视图-控制器&#xff09;模式。是一种软件架构模式。 分层描述Model&#xff08;模型&#xff09;它是应用程序的主体部分&#xff0c;主要由以下 2 部分组成&#xff1a; 实体类 Bean&#xff1a;专…

(4.7-4.13)【大数据新闻速递】上海、广州、青海、贵阳大力发展大数据产业;2026年中国大数据市场规模预计达365亿美元

01【贵阳大数据交易所发布全国首个交易激励计划】 4月6日&#xff0c;贵阳大数据交易所发布了“交易激励计划”&#xff0c;旨在提高数据场内交易的吸引力&#xff0c;解决交易入场难的问题。该计划是落实《关于构建数据基础制度更好发挥数据要素作用的意见》和《关于支持贵州…

阿里云服务器网络收发包PPS性能25万/80万/100万PPS详解

阿里云服务器ECS网络收发包PPS是什么&#xff1f;云服务器PPS多少合适&#xff1f;网络收发包PPS是指云服务器每秒可以处理的网络数据包数量&#xff0c;单位是PPS即packets per second每秒发包数量。阿里云百科来详细说下阿里云服务器网络收发包PPS性能参数表&#xff0c;以及…

Vue 事件处理器

文章目录 Vue 事件处理器事件修饰符按键修饰符 Vue 事件处理器 事件监听可以使用 v-on 指令&#xff1a; v-on <div id"app"><button v-on:click"counter 1">增加 1</button><p>这个按钮被点击了 {{ counter }} 次。</p>…

入门神经网络——浅层神经网络

文章目录 一、基础知识1.浅层神经网络介绍2.浅层神经网络的正向传播3.反向传播 二、浅层神经网络代码实例 一、基础知识 1.浅层神经网络介绍 此次构件浅层神经网络&#xff0c;相比于单神经元&#xff0c;浅层神经网络拥有多个神经元&#xff0c;因此又可以称为多神经元网络&…