4.3 Linux的中断处理流程

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

4.3 Linux的中断处理流程

先上图,一图胜千言!

图中心的蓝色部分,是Linux通用中断子系统的核心数据结构。图中只是列出各个数据结构的核心成员。在上一个章节,已经讲过了这些数据结构建立的时机,就不再重复描述。重点还是讨论图中外围画出的中断处理流程。

第一步,从hwirq发生到转换成virq(图左侧从上到下)

当EL1_IRQ或EL0_IRQ发生后,会调用全局函数指针handle_arch_irq。以ARM的GIC V3中断控制器为例,全局函数指针handle_arch_irq指向irq-gic-v3.c中定义的gic_handle_irq

在《4.2.3 根据DTS完成中断控制器初始化》中,描述了GIC V3中断控制器的初始化。在初始化过程中,irq-gic-v3.c中调用set_handle_irq(gic_handle_irq),设置handle_arch_irq指向gic_handle_irq,具体的调用过程如下。

start_kernel <init/main.c>
->	init_IRQ <arch/arm64/kernel/irq.c>->	irqchip_init <drivers/irqchip/irqchip.c>->	of_irq_init(__irqchip_of_table) <drivers/of/irq.c>->	gic_of_init <drivers/irqchip/irq-gic-v3.c>->	gic_init_bases <drivers/irqchip/irq-gic-v3.c>->	set_handle_irq(gic_handle_irq)

如果某个中断源被触发,GIC会将IAR寄存器(Interrupt Acknowledge Register)中该中断源对应的bit置1。在gic_handle_irq中,调用gic_read_iar得到hwirq。

每个GIC V3中断控制器,在初始化的时候,都会创建一个irq_domain,并记录在自己的私有数据结构gic_data.domain。

根据上图中的第385行代码,对于PPI/SPI中断(大于15,小于1020)和LPI中断(大于等于8192),会走到handle_domain_irq处理,会传入irq_domain和hwirq做为入参。已知irq_domain和hwirq,怎么找出virq?内核已经定义好了一个API:irq_find_mapping。

对于GIC V3来说,创建domain的调用过程:irq_domain_create_tree->__irq_domain_add(fwnode, 0, ~0, 0, ops, host_data),其中第2个参数为domain->revmap_size赋值0;第4个参数为domain->revmap_direct_max_irq赋值0。所以,最终总是使用基数树irq_domain->revmap_tree来完成反向映射的查找。

至此,hwirq转换成了virq!

第二步,找出virq对应的irq_desc(图底部从左到右)

hwirq转换成了virq之后,handle_domain_irq调用generic_handle_irq处理virq。每个virq,都有自己对应的irq_desc,怎么找到呢?内核定义了一个API: irq_to_desc。在我当前的内核中,定义了CONFIG_SPARSE_IRQ,所以它的本质就是查找基数树irq_desc_tree,得到irq_desc。

第三步,执行中断流控处理程序(图右侧从下向上)

找到了irq_desc,紧接着generic_handle_irq调用generic_handle_irq_desc,执行irq_desc->handle_irq(desc)。

这个回调函数是什么?

irq_flow_handler_t handle_irq是irq_desc的一个成员,从字面可以翻译成:中断流控函数,也有人翻译成中断high level处理函数。

中断流控(Interrupt Flow Control)是指在Linux内核中合理且正确地管理连续发生的中断事件。具体而言,当一个中断正在处理过程中,如果相同的中断再次发生,系统需要决定如何处理这一新中断,包括何时屏蔽中断、何时恢复中断、以及何时向中断控制器发送响应信号等。针对不同类型的中断电气特性(如电平触发和边沿触发),Linux提供了标准化的中断流控处理函数。这些处理函数最终会将中断控制权传递给驱动程序在注册中断时提供的处理函数或中断线程中,这些函数均定义在 kernel/irq/chip.c 文件中:

handle_simple_irq:用于处理简单的中断流控,适用于不需要复杂流控机制的中断场景。

handle_level_irq:用于处理电平触发(Level-triggered)中断的流控,确保在中断源未清除前屏蔽中断,防止重复处理。

handle_edge_irq:用于处理边沿触发(Edge-triggered)中断的流控,确保中断处理函数能够及时响应瞬时的中断信号。

handle_fasteoi_irq:用于需要快速响应结束中断(End of Interrupt, EOI)信号的中断控制器,例如GICv3,CPU只需要在每次处理完中断后发出一个end of interrupt(eoi),我们无需关注何时mask,何时unmask。

handle_percpu_irq:用于处理仅在单个CPU上响应的中断,确保这些中断只在指定的CPU上处理,避免跨CPU通信的开销。

handle_nested_irq:用于处理嵌套中断,特别是那些使用中断线程的中断,确保中断处理的顺序性和正确性。

什么时候设置的?

针对GIC V3,在每一个设备的中断初始化过程中,都会设置中断流控函数。

  1. SGIs: 不需要设置
  2. PPIs: 设置为handle_percpu_devid_irq,例如timer中断
  3. SPIs: 设置为handle_fasteoi_irq
  4. LPIs: 设置为handle_fasteoi_irq

 以handle_fasteoi_irq为例,经过层层调用,到达__handle_irq_event_percpu,开始执行已经注册的中断处理程序。

第四步,执行中断处理程序(图的终点)

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{irqreturn_t retval = IRQ_NONE;  // 初始化返回值为IRQ_NONEunsigned int irq = desc->irq_data.irq;  // 获取中断号struct irqaction *action;  // 指向中断动作结构的指针record_irq_time(desc);  // 记录中断处理时间for_each_action_of_desc(desc, action) {  // 遍历所有注册的中断处理函数irqreturn_t res;  // 存储当前处理函数的返回结果trace_irq_handler_entry(irq, action);  // 跟踪中断处理入口res = action->handler(irq, action->dev_id);  // 调用中断处理函数trace_irq_handler_exit(irq, action, res);  // 跟踪中断处理出口if (WARN_ONCE(!irqs_disabled(), "irq %u handler %pF enabled interrupts\n", irq, action->handler))local_irq_disable();  // 如果中断处理函数启用了中断,则发出警告并禁用中断switch (res) {  // 根据处理函数的返回结果进行处理case IRQ_WAKE_THREAD:/** 捕获那些返回WAKE_THREAD但没有设置线程函数的驱动程序*/if (unlikely(!action->thread_fn)) {warn_no_thread(irq, action);  // 发出警告break;}__irq_wake_thread(desc, action);  // 唤醒中断线程/* Fall through to add to randomness */case IRQ_HANDLED:*flags |= action->flags;  // 更新标志位break;default:break;}retval |= res;  // 更新返回值}return retval;  // 返回最终的处理结果
}

关键点是通过for_each_action_of_desc(desc, action)遍历action链表,同步调用action->handler,然后根据返回值判断是否要唤醒中断线程action->thread_fn。

那么action链表是如何生成的?action->handler和action->thread_fn是如何赋值的?

request_threaded_irq(unsigned int irq, irq_handler_t handler,

             irq_handler_t thread_fn,

             unsigned long flags, const char *name, void *dev);

static inline int __must_check

request_irq(unsigned int irq, irq_handler_t handler, unsigned long flags,

        const char *name, void *dev)

{

    return request_threaded_irq(irq, handler, NULL, flags, name, dev);

}

 传统的request_irq在注册中断时,需要注册action->handler,并不会注册action->thread_fn(设置为NULL)。

request_threaded_irq在注册中断时:

  1. handler和thread_fn不能同时为NULL
  2. 如果handler为NULL,则设置为默认的irq_default_primary_handler
int request_threaded_irq(unsigned int irq, irq_handler_t handler,irq_handler_t thread_fn, unsigned long irqflags,const char *devname, void *dev_id)
{…………if (!handler) {if (!thread_fn)return -EINVAL;handler = irq_default_primary_handler;}action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);if (!action)return -ENOMEM;action->handler = handler;action->thread_fn = thread_fn;
…………
}

注意,中断注册时传入的参数irq是virq而不是hwirq。如果多个设备共享一个hwirq,则可以多次调用中断注册函数,向同一个virq注册多个irqaction,形成irqaction链表。注册时,必须设置IRQF_SHARED标记。当中断发生时,最终会遍历irqaction链表。

小结一下:

从一副图说起,以Linux通用中断子系统的数据结构为中心,分四步描述了Linux中断处理流程:

第一步,从hwirq发生到转换成virq(图左侧从上到下)

第二步,找出virq对应的irq_desc(图底部从左到右)

第三步,执行中断流控处理程序(图右侧从下向上)

第四步,执行中断处理程序(图的终点)

 

点击查看系列文章 =》 Interrupt Pipeline系列文章大纲-CSDN博客

原创不易,需要大家多多鼓励!您的关注、点赞、收藏就是我的创作动力!

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

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

相关文章

告别项目混乱,自定义工作项类型驱动项目管理进化

在项目管理中&#xff0c;工作项类型是用于分类和管理项目任务的基本构建块。如瀑布管理和敏捷管理&#xff0c;都有其特定的工作项类型来支持其独特的流程和实践&#xff1a; 敏捷管理的工作项类型 敏捷管理中的工作项类型强调迭代和增量开发&#xff0c;以用户故事为核心&a…

JAVA学习日记(十一) 常用API

一、Math //开平方根 public static double sqrt(double a); //返回结果 //开立方根 public static double cbrt(double a); 水题&#xff1a; public class Main {public static void main(String[] args) {//统计一共有多少个水仙花数 : abca^3b^3c^3abc// aabc/100%10//…

C++ 的异常处理详解

C 的异常处理详解 在编程过程中&#xff0c;错误和异常是不可避免的&#xff0c;合理的异常处理机制能够提高程序的健壮性。在 C 中&#xff0c;异常机制为捕获和处理错误提供了一种结构化的方式。本文将对 C 的异常处理进行详细探讨&#xff0c;包括异常的概念、如何抛出和捕…

IP协议知识点总结

IP协议主要分为三个 1. 地址管理 每个网络上的设备, 要能分配一个唯一的地址 2. 路由选择 小A 给小B 发消息, 具体应该走什么路线 3. 地址管理 IP 地址. 本质上是一个 32 位的整数 通常将, 32 位的整数使用点分十进制来表示, 如 192.168.1.1 一共可以表示 42 亿 9 千万个地址…

秒杀优化(异步秒杀,基于redis-stream实现消息队列)

目录 秒杀优化一&#xff1a;异步秒杀1&#xff1a;思路2&#xff1a;实现 二&#xff1a;redis实现消息队列1&#xff1a;什么是消息队列2&#xff1a;基于list结构实现消息队列3&#xff1a;基于pubsub实现消息队列4&#xff1a;基于stream实现消息队列5&#xff1a;stream的…

小新学习k8s第六天之pod详解

一、资源限制 Pod是k8s中的最小的资源管理组件&#xff0c;pod也是最小化运行容器化应用的资源对象。一个Pod代表着集群中运行的一个进程。k8s中其他大多数组件都是围绕着Pod来进行支撑和扩展Pod功能的&#xff0c;例如&#xff0c;用于管理Pod运行的StatefulSet和Deployment等…

Solana 代币 2022 — Transfer Hook

从零到英雄的 Solana 代币 2022 — Transfer Hook Token 2022 计划引入了几项令人兴奋的扩展&#xff0c;增强了铸造和代币账户的功能。在这些功能中&#xff0c;我个人最喜欢的是Transfer Hook &#xff08;转账钩子&#xff09; 。 想象时间 让我们戴上想象的帽子&#xf…

自定义类型:结构体(一)

一 . 结构体的相关概念 结构体&#xff0c;无需多言&#xff0c;是我们的老朋友了&#xff0c;我们之前就学习过一些有关结构体的知识&#xff0c;今天我们就来正式认识一下这个朋友 结构体属于一种自定义类型&#xff0c;在我们C语言中&#xff1a;自定义类型并非只有结构体…

使用匿名管道时出现程序一直运行问题

父进程创建两个子进程&#xff0c;父子进程之间利用管道进行通信。要求能显示父进程、子进程各自的信息&#xff0c;体现通信效果。(源程序pipe_1.c) 一开始&#xff0c;我忘了初始化pipe,很傻*的直接把fd当管道使&#xff0c;出现了儿子喊爸爸"i am your father."的…

协程4 --- 一个特殊的栈溢出例子

文章目录 代码运行结果分析 代码 先看下面这个程序流程&#xff1a; 有个长度位24的字符数组buffer&#xff0c;前面16个字符初始化。 把attack函数的地址复制到后面8个字符&#xff08;编译成64位程序&#xff0c;指针大小为8Byte&#xff09;。 打印信息&#xff1a;do Some…

C++用string实现字符串相加

. - 力扣&#xff08;LeetCode&#xff09; -》》》》》题目链接 实现思路&#xff1a;计算数字符串长度并用数组的方式计算出字符位置&#xff0c;用字符的ask码‘0’计算出字符本身。 class Solution { public:string addStrings(string num1, string num2) {string str;int…

03 Oracle进程秘籍:深度解析Oracle后台进程体系

文章目录 Oracle进程秘籍&#xff1a;深度解析Oracle后台进程体系一、Oracle后台进程概览1.1 DBWn&#xff08;Database Writer Process&#xff09;1.2 LGWR&#xff08;Log Writer Process&#xff09;1.3 SMON&#xff08;System Monitor Process&#xff09;1.4 PMON&#…

【大数据学习 | kafka高级部分】文件清除原理

2. 两种文件清除策略 kafka数据并不是为了做大量存储使用的&#xff0c;主要的功能是在流式计算中进行数据的流转&#xff0c;所以kafka中的数据并不做长期存储&#xff0c;默认存储时间为7天 那么问题来了&#xff0c;kafka中的数据是如何进行删除的呢&#xff1f; 在Kafka…

浏览器存储策略解析(三)Local/sessionStorage实战:如何查看本地浏览器上数据

物理意义上的localStorage/sessionStorage在哪里 我们都知道&#xff0c;localStorage存于本地&#xff0c;sessionStorage存于会话&#xff0c;这是见名知意可以得到的。但是在物理层面他们究竟存储在哪里呢&#xff1f; localStorage和sessionStorage一样&#xff0c;是存储…

设计模式讲解02—责任链模式(Chain)

1. 概述 定义&#xff1a;责任链模式是一种行为型模式&#xff0c;在这个模式中&#xff0c;通常创建了一个接收者对象的链来处理请求&#xff0c;该请求沿着链的顺序传递。直到有对象处理该请求为止&#xff0c;从而达到解耦请求发送者和请求处理者的目的。 解释&#xff1a;责…

判断二叉搜索树(递归)

给你一个二叉树的根节点 root &#xff0c;判断其是否是一个有效的二叉搜索树。binary search tree (BST) 有效 二叉搜索树定义如下&#xff1a; 节点的左子树只包含 小于 当前节点的数。节点的右子树只包含 大于 当前节点的数。所有左子树和右子树自身必须也是二叉搜索树。 …

私有化视频平台EasyCVR海康大华宇视视频平台视频诊断技术是如何实时监测视频质量的?

在现代视频监控系统中&#xff0c;确保视频流的质量和稳定性至关重要。随着技术的进步&#xff0c;视频诊断技术已经成为实时监测视频质量的关键工具。这种技术通过智能分析算法对视频流进行实时评估和处理&#xff0c;能够自动识别视频中的各种质量问题&#xff0c;并给出相应…

大语言模型(LLM)量化基础知识(一)

请大家关注我的知乎博客&#xff1a;- 派神 - - 知乎 随着大型语言模型 (LLM) 的参数数量的增长,与其支持硬件&#xff08;加速器内存&#xff09;增长速度之间的差距越来越大&#xff0c;如下图所示&#xff1a; 上图显示&#xff0c;从 2017 年到 2022 年&#xff0c;语言模…

【comfyui教程】ComfyUI 现已支持 Stable Diffusion 3.5 Medium!人人都能轻松上手的图像生成利器

前言 ComfyUI 现已支持 Stable Diffusion 3.5 Medium&#xff01;人人都能轻松上手的图像生成利器 大家翘首以盼的Stable Diffusion 3.5 Medium模型终于来了&#xff01;就在今天&#xff0c;Stability AI 正式推出了这款“亲民版”平衡模型&#xff0c;让创作者们得以在消费…

大模型微调技术 --> LoRA 系列之 AdaLoRA

AdaLoRA 1.摘要 之前的微调方法(如低秩更新)通常将增量更新的预算均匀地分布在所有预训练的权重矩阵上&#xff0c;并且忽略了不同权重参数的不同重要性。结果&#xff0c;微调结果不是最优的。 为了弥补这一差距&#xff0c;我们提出了AdaLoRA&#xff0c;它根据权重矩阵的…