【IMX6ULL驱动开发学习】18.中断下半部(tasklet、工作队列、中断线程化)

news2024/12/23 0:12:52

下图表述了Linux内核的中断处理机制,为了在中断执行时间尽量短和中断处理需完成的工作尽量大之间找到一
个平衡点, Linux将中断处理程序分解为两个半部: 顶半部(Top Half) 和底半部(Bottom Half) 。
在这里插入图片描述
顶半部: 用于完成尽量少的比较紧急的功能, 它往往只是简单地读取寄存器中的中断状态, 并在清除中断标志后就
进行“登记中断”的工作。 “登记中断 意味着将底半部处理程序挂到该设备的底半部执行队列中去。 这样, 顶半部
执行的速度就会很快, 从而可以服务更多的中断请求。

底半部: 几乎做了中断处理程序所有的事情, 而且可以被新的中断打断, 这也是底半部和顶半部的最大不同, 因为顶半部往往被设计成不可中断。 底半部相对来说并不是非常紧急的, 而且相对比较耗时, 不在硬件中断服务程序中执行。


Linux实现底半部的机制主要有tasklet、 工作队列、 软中断和线程化irq

先来个比较:

中断下半部机制上下文是否可被打断是否可睡眠实时性
tasklet运行于软中断上下文可被硬中断打断,不会被定时器中断打断不可睡眠会阻塞应用程序
工作队列运行于内核线程上下文可被硬中断、定时器中断打断可睡眠同一个链表中可能挂多个work
所以实时性会降低
threaded_irq运行于内核线程上下文可被硬中断、定时器中断打断可睡眠对于每个中断都有独立的内核线程
实时性高

1. tasklet

tasklet的使用较简单, 它的执行上下文是软中断, 执行时机通常是顶半部返回的时候。 我们只需要定义tasklet及
其处理函数, 并将两者关联则可, 例如:

/* 定义一个处理函数 */
void my_tasklet_func(unsigned long); 
/* 定义一个 tasklet 结构 my_tasklet , 与 my_tasklet_func(data) 函数相关联 */
DECLARE_TASKLET(my_tasklet, my_tasklet_func, data);/* 定义一个 tasklet_struct 结构体 */
struct tasklet_struct tasklet;
/* 定义一个处理函数 */
void my_tasklet_func(unsigned long); 
/* 关联 tasklet 与 处理函数 */
void tasklet_init(struct tasklet_struct *t,
		          void (*func)(unsigned long), 
		          unsigned long data)

在需要调度tasklet的时候引用一个tasklet_schedule() 函数就能使系统在适当的时候进行调度运行:

tasklet_schedule(struct tasklet_struct *tasklet);

销毁 tasklet

tasklet_kill(struct tasklet_struct *tasklet);

taskelet 运行于软中断上下文,所以不能睡眠,会阻塞应用程序,可被外部中断打断,不会被内核定时器中断打断

/* 中断下半部处理函数 不能睡眠
   可以被外部中断打断,但不会被定时中断打断 */
static void my_tasklet_func(unsigned long arg)
{
	int i = 0;
	printk("my_tasklet_func gpio = %d\n", (int)arg);
	/* 循环期间,APP程序得不到执行 */
	while(i < 500){
		printk("%d ",i++);

		if(i % 20 == 0)
			printk("\n");
	}
	printk("\n");
}

测试结果:
tasklet 可以被按键中断,但不能被定时器中断,也不能睡眠(测试了添加msleep后,加载驱动直接一直输出报错)

在这里插入图片描述

tasklet 使用模板

/* 定义 tasklet 和底半部函数并将它们关联 */
void xxx_do_tasklet(unsigned long);
DECLARE_TASKLET(xxx_tasklet, xxx_do_tasklet, 0);

/* 中断处理底半部 */
void xxx_do_tasklet(unsigned long)
{
 ...
}

/* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
 ...
 tasklet_schedule(&xxx_tasklet);
 ...
}

/* 设备驱动模块加载函数 */
int __init xxx_init(void)
 {
 ...
 /* 申请中断 */
 result = request_irq(xxx_irq, xxx_interrupt,0, "xxx", NULL);
 ...
 return IRQ_HANDLED;
}

/* 设备驱动模块卸载函数 */
void __exit xxx_exit(void)
{
 ...
 /* 释放中断 */
 free_irq(xxx_irq, xxx_interrupt);
 ...
}

2. 工作队列

工作队列的使用方法和tasklet非常相似, 但是工作队列的执行上下文是内核线程, 因此可以调度和睡眠。 下面的
代码用于定义一个工作队列和一个底半部执行函数:

struct work_struct my_wq; /* 定义一个工作队列 */

void my_wq_func(struct work_struct *work); /* 定义一个处理函数 */

通过INIT_WORK() 可以初始化这个工作队列并将工作队列与处理函数绑定:

/* 初始化工作队列并将其与处理函数绑定 */
INIT_WORK(&my_wq, my_wq_func);

与tasklet_schedule() 对应的用于调度工作队列执行的函数为schedule_work() , 如:

/* 调度工作队列执行 */
schedule_work(&my_wq);

在这里插入图片描述
假设有两个cpu,当workqueue有新任务时,会将任务放进cpu0和cpu1左侧的work链表中(所有cpu的work链表都放),调度任务时从两个cpu的work_pool中选择一个普通优先级线程放入执行(总共只选一个)

工作队列使用模板

/* 定义工作队列和关联函数 */
struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);

/* 中断处理底半部 */
void xxx_do_work(struct work_struct *work)
{
 ...
}

/* 中断处理顶半部 */
irqreturn_t xxx_interrupt(int irq, void *dev_id)
{
 ...
 schedule_work(&xxx_wq);
 ...
 return IRQ_HANDLED;
}

/* 设备驱动模块加载函数 */
int xxx_init(void)
{
 ...
 /* 申请中断 */
 result = request_irq(xxx_irq, xxx_interrupt,0, "xxx", NULL);
 ...
 /* 初始化工作队列 */
 INIT_WORK(&xxx_wq, xxx_do_work);
 ...
}

/* 设备驱动模块卸载函数 */
void xxx_exit(void)
{
 ...
 /* 释放中断 */
 free_irq(xxx_irq, xxx_interrupt);
 ...
}

测试结果:
工作队列可以被按键和定时器中断,也可以睡眠

在这里插入图片描述
在这里插入图片描述


3. 软中断(Softirq)

  • 软中断(Softirq) 也是一种传统的底半部处理机制, 它的执行时机通常是顶半部返回的时候, tasklet是基于软中断实现的, 因此也运行于软中断上下文。在Linux内核中, 用 softirq_action 结构体表征一个软中断, 这个结构体包含软中断处理函数指针和传递给该函数的参数。 使用 open_softirq() 函数可以注册软中断对应的处理函数, 而 raise_softirq() 函数可以触发一个软中断。
  • 软中断和tasklet运行于软中断上下文, 仍然属于原子上下文的一种, 而工作队列则运行于进程上下文。 因此, 在
    软中断和tasklet处理函数中不允许睡眠, 而在工作队列处理函数中允许睡眠
  • local_bh_disable() 和local_bh_enable() 是内核中用于禁止和使能软中断及tasklet底半部机制的函数。
    内核中采用softirq的地方包括HI_SOFTIRQ、 TIMER_SOFTIRQ、 NET_TX_SOFTIRQ、 NET_RX_SOFTIRQ、
    SCSI_SOFTIRQ、 TASKLET_SOFTIRQ等, 一般来说, 驱动的编写者不会也不宜直接使用softirq
  • 总结一下硬中断、 软中断和信号的区别: 硬中断是外部设备对CPU的中断软中断是中断底半部的一种处理机制, 而信号则是由内核(或其他进程) 对某个进程的中断
  • 在涉及系统调用的场合, 人们也常说通过软中断(例如ARM为swi) 陷入内核, 此时软中断的概念是指由软件指令引发的中断, 和我们这个地方说的softirq是两个完全不同的概念, 一个是software, 一个是soft。
  • 需要特别说明的是, 软中断以及基于软中断的tasklet如果在某段时间内大量出现的话, 内核会把后续软中断放入
    ksoftirqd内核线程中执行。 总的来说, 中断优先级高于软中断软中断又高于任何一个线程。 软中断适度线程
    化, 可以缓解高负载情况下系统的响应。

4. threaded_irq(中断线程化)

在内核中, 除了可以通过request_irq() 、 devm_request_irq() 申请中断以外, 还可以通过
request_threaded_irq() 和devm_request_threaded_irq() 申请。 这两个函数的原型为:

int request_threaded_irq(unsigned int irq, irq_handler_t handler, irq_handler_t thread_fn,
						 unsigned long flags, const char *name, void *dev);
						 
int devm_request_threaded_irq(struct device *dev, unsigned int irq,
							  irq_handler_t handler, irq_handler_t thread_fn,
							  unsigned long irqflags, const char *devname,
							  void *dev_id);
  • 由此可见, 它们比request_irq() 、 devm_request_irq() 多了一个参数thread_fn。 用这两个API申请中断的时
    候, 内核会为相应的中断号分配一个对应的内核线程。 注意这个线程只针对这个中断号, 如果其他中断也通过
    request_threaded_irq() 申请, 自然会得到新的内核线程。
  • 参数handler对应的函数执行于中断上下文thread_fn参数对应的函数则执行于内核线程。 如果handler结束的时候, 返回值是IRQ_WAKE_THREAD, 内核会调度对应线程执行thread_fn对应的函数。
  • request_threaded_irq() 和devm_request_threaded_irq() 支持在irqflags中设置IRQF_ONESHOT标记, 这样内核会
    自动帮助我们在中断上下文中屏蔽对应的中断号而在内核调度thread_fn执行后, 重新使能该中断号。 对于我们
    无法在上半部清除中断的情况, IRQF_ONESHOT特别有用, 避免了中断服务程序一退出, 中断就洪泛的情况。
  • handler参数可以设置为NULL, 这种情况下, 内核会用默认的irq_default_primary_handler() 代替handler, 并会使
    用IRQF_ONESHOT标记。 irq_default_primary_handler() 定义为:
/*
* Default primary interrupt handler for threaded interrupts. Is
* assigned as primary handler when request_threaded_irq is called
* with handler == NULL. Useful for oneshot interrupts.
*/
static irqreturn_t irq_default_primary_handler(int irq, void *dev_id)
{
	return IRQ_WAKE_THREAD;
}

threaded_irq 使用案例 (可通过<asm/current.h>头文件的current对象获取线程名和pid)

/* 定义工作队列和关联函数 */
struct work_struct xxx_wq;
void xxx_do_work(struct work_struct *work);

/* 中断线程化:存在于内核线程 */
static irqreturn_t key_threaded_func_handler(int irq, void *dev)
{
	int i = 0;
	printk("the process is\"%s\"(pid %d)\n",current->comm, current->pid);  //打印出线程名和pid
	while(i < 5){
		printk("%d ",i++);
		msleep(500);
	}
	printk("\n");
	return IRQ_HANDLED;
}


/* 中断处理顶半部 */
static irqreturn_t key_irq_handler(int irq, void *dev)
{
	struct gpioirq *girq = dev;
 	...
	return IRQ_WAKE_THREAD;	 
}


/* 设备驱动模块加载函数 */
int xxx_init(void)
{
	...
	/* 申请中断 */
	/* 中断下半部:threaded_irq        IRQF_ONESHOT:在上下文中屏蔽对应中断号 */
	request_threaded_irq(xxx_irq, key_irq_handler, key_threaded_func_handler, 
							IRQF_TRIGGER_FALLING, "hc_key_irq", xxx_arg);
	...
}

/* 设备驱动模块卸载函数 */
void xxx_exit(void)
{
	...
	/* 释放中断 */
	free_irq(xxx_irq, xxx_arg);
	...
}

测试结果:
threaded_irq可以被中断、定时器打断,可以睡眠

在这里插入图片描述

PS:扩展一个 container_of 函数,它可以倒推导一个结构体的起始地址
在这里插入图片描述

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

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

相关文章

centos7安装、使用webbench

简言 1. linux下web服务器性能压测工具有很多&#xff0c;webbench就很不错&#xff0c;而且安装使用都很简单 2. webbench不但能对静态页面的压测&#xff0c;还能对动态页面&#xff08;ASP,PHP,JAVA,CGI&#xff09;进行压测。而且支持对含有SSL的安全网站&#xff0c;例如…

Spring cloud alibaba 整合 Sentinel

Sentinel详解 Docker安装1、拉取镜像2、运行容器访问 整合 spring-cloud-alibaba1、引入Maven依赖2、配置控制台3、编写控制器4、启动Sentinel访问自定义异常处理统一异常处理 整合 OpenFeign引入Maven依赖&#xff1a; 配置&#xff1a;编写 Feign 实现指定 Feign 容错类控制器…

ROS2在改造ros1时,报警相关库异常排查

一、在make时&#xff0c;存在以下报警&#xff0c;检查h中是已经包含相关的头文件了&#xff0c;并且也已改为ros2的格式。 二、解决&#xff1a; 检查发现&#xff0c;在CMakelists.txt中未添加相关依赖包&#xff0c;重新添加后&#xff0c;报警解除&#xff0c;编译通过。…

商家们的“疗效”焦虑,巨量引擎、阿里妈妈、腾讯广告们都在怎么满足?

文 | 螳螂观察 作者 | 青月 有人的地方就有营销。 虽然这是一门永不褪色的“生意”&#xff0c;但在增量见顶、红利消失的互联网&#xff0c;数字营销变得越来越听不见“水响”。 就连在号称“史上最卷”的今年618&#xff0c;同台竞技的各大数字营销服务商都在强调自己的“…

difflib 比较文本相似度,找出错误值

在日常的数据分析过程中&#xff0c;我们可能会遇到这样的问题。在处理数据时&#xff0c;有的文本内容是同一类目&#xff0c;但是由于手工输入错误 或者大小写的问题&#xff0c;可能会造成将产品分到不同的类目下&#xff0c;这时候就需要对数据进行清洗。如何实现快速比较…

Selenium基础篇之屏幕截图方法

文章目录 前言一、用途1.捕获页面错误2.调试测试用例3.展示测试结果4.记录页面状态 二、方法1. save_screenshot2. get_screenshot_as_file3. get_screenshot_as_png4. get_screenshot_as_base64 总结 前言 大家好&#xff0c;我是空空star&#xff0c;本篇给大家分享一下Selen…

IDEA+SpringBoot + Mybatis + Shiro+Bootstrap+Mysql智慧仓库系统

IDEASpringBoot Mybatis ShiroBootstrapMysql智慧仓库系统 一、系统介绍1.环境配置 二、系统展示1. 管理员登录2.主页3.货位一览4.入库单5. 库存明细6. 呆滞过期报表7. 转库记录8.入库记录9.出库记录10.出库单11.物料信息12.仓库设置13.用户管理14.操作员管理15.角色管理16.账…

Python实现SMOGN算法解决不平衡数据的回归问题

本文介绍基于Python语言中的smogn包&#xff0c;读取.csv格式的Excel表格文件&#xff0c;实现SMOGN算法&#xff0c;对机器学习、深度学习回归中&#xff0c;训练数据集不平衡的情况加以解决的具体方法。 在不平衡回归问题中&#xff0c;样本数量的不均衡性可能导致模型在预测…

解决Navicat连接Oracle报ORA-28547

《进入Oracle官网》 下载Instant Client Products --------------》Oracle Database download database --------------》Download Oracle Database X Instant Client - C/C Drivers (OCI, OCCI, ODBC) and Utilities Download Now 根据自己的操作系统下载对应的Oracle …

【深入浅出Apache Jackrabbit】第三章 Repository 配置文件

系列文章目录 第一章 初见 Apache Jackrabbit 第二章 Apache Jackrabbit 入门 第三章 Repository 配置文件 第四章 Apache Jackrabbit 文件存储 第五章 Apache Jackrabbit 版本管理 文章目录 系列文章目录一、创建一个 Repository 配置文件二、初始化 Repository Apache Jackr…

基于单片机的智能温控风扇的设计与实现

功能介绍 以STM32单片机作为主控系统&#xff1b; 液晶显示当前温度&#xff0c;风速等级&#xff0c;自动手动模式息&#xff1b; DS18B20检测当前的温度&#xff1b; 按键设置温度上限&#xff1b; 手动模式下&#xff1a; 直接控制风扇档位&#xff0c;可以开启/关闭风扇&a…

DragGAN在Windows11中下载安装

DragGAN是一种颠覆性的新型图像生成和编辑技术&#xff0c;它实现了前所未有的用户对图像创作过程的控制能力。使用DragGAN&#xff0c;用户可以用鼠标拖放的方式在空白画布上概括构思图像&#xff0c;系统会自动生成图像细节。例如&#xff0c;用户可以拖拽画一个矩形作为“汽…

js原生实现楼层效果

html文件 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</title><style>* {m…

TPU编程竞赛系列|第九届 “互联网+”大学生创新创业大赛产业命题赛道,算能6项命题入选!

近日&#xff0c;第九届中国国际“ 互联a网 ” 大学生创新创业大赛产业命题正式公布&#xff0c;算能提交的六 项企业命题成功入选正式赛题。算能六项赛题主要围绕国产 TPU 芯片的边缘计算系统和 RISC-V 架构处理器来设计&#xff0c;且为参赛选手提供了超强算力开发板等硬件资…

前端vue入门(纯代码)29_路由的params参数

手机里的钞票薄情寡义,身上的赘肉不离不弃&#xff01; 【27.Vue Router--路由的params参数】 params属性起到的作用和query差不多&#xff0c;都是用于传递和接收参数&#xff0c;只不过&#xff0c;它是在src/router/index.js文件中进行配置 // 该文件专门用于创建整个应用…

逆向某安卓游戏native层登录协议的过程记录

前言 主要参考白龙哥的unidbg学习&#xff1a;SO逆向实战十三篇 用到工具&#xff1a; frida(HOOK看参数) unidbg&#xff08;模拟&#xff09; ida&#xff08;静态分析&#xff09; charles&#xff08;抓包&#xff09; 准备 此处省略一大堆获取So文件流程&#xff0c;…

单片机编程要求的C语言水平和技能

如果你想从事单片机工作&#xff0c;你需要具备一定水平的C语言编程技能。以下是一些建议的技能和知识&#xff0c;可以帮助你为单片机编程做好准备&#xff1a; 熟悉C语言基础&#xff1a;掌握C语言的基本语法、数据类型、控制结构和函数等。这是你开始学习单片机编程的基础。…

实操:用Flutter构建一个简单的微信天气预报小程序

​ 微信小程序是一种快速、高效的开发方式&#xff0c;Flutter则是一款强大的跨平台开发框架。结合二者&#xff0c;可以轻松地开发出功能丰富、用户体验良好的微信小程序。 这里将介绍如何使用Flutter开发一个简单的天气预报小程序&#xff0c;并提供相应的代码示例。 1. 准备…

《C语言初阶篇》循环语句还没搞懂?这篇文章带你轻松学会循环语句!

&#x1f3ac; 鸽芷咕&#xff1a;个人主页 &#x1f525; 个人专栏:《快速入门C语言》《C语言初阶篇》 ⛺️生活的理想&#xff0c;就是为了理想的生活! 文章目录 前言&#x1f4ac; 循环语句的介绍&#x1f4ac; while 语句的介绍&#x1f4ad; while中的break&#x1f4ad;…

numpy 多项式函数回归与插值拟合模型;ARIMA时间序列模型拟合

参考&#xff1a; https://blog.csdn.net/mao_hui_fei/article/details/103821601 1、多项式函数回归拟合 import numpy as np from scipy.optimize import leastsq import pylab as plx np.arange(1, 17, 1) y np.array([4.00, 6.40, 8.00, 8.80, 9.22, 9.50, 9.70, 9.86,…