Linux中断管理:(一)中断号的映射

文章说明:

  • Linux内核版本:5.0

  • 架构:ARM64

  • 参考资料及图片来源:《奔跑吧Linux内核》

  • Linux 5.0内核源码注释仓库地址:

    zhangzihengya/LinuxSourceCode_v5.0_study (github.com)

1. 中断控制器

Linux 内核支持众多的处理器架构,因此从系统角度来看,Linux内核的中断管理可以分成如下4层:

  • 硬件层,如CPU和中断控制器的连接
  • 处理器架构管理层,如CPU中断异常处理
  • 中断控制器管理层,如IRQ号的映射
  • Linux 内核通用中断处理器层,如中断注册和中断处理

不同的架构对中断控制器有着不同的设计理念:

  • ARM架构采用通用中断控制器 (Generic Interrupt Controller, GIC)
  • x86架构采用高级可编程中断控制器 (Advanced Programmable Interrupt Controller, APIC)

本文以 ARM Vexpress V2P-CAIS-CA7 平台为例来介绍中断管理的实现,它支持 Cortex-A15 和 Cortex-A7 两个CPU簇,中断控制器采用GIC-400,支持GIC Version 2 (GIC-V2),如下图所示:

在这里插入图片描述

对于一个中断来说,支持多个中断状态

  • 不活跃(inactive)状态:中断处于无效状态
  • 等待(pending)状态:中断处于有效状态,但是等待CPU响应该中断
  • 活跃(active)状态:GPU已经响应中断
  • 活跃并等待(active and pending)状态:CPU正在响应中断,但是该中断源又发送中断过来

对于GIC来说,为每一个硬件中断源分配一个中断号,这就是硬件中断号。GIC会为支持的中断类型分配中断号范围,如下表所示:

在这里插入图片描述

  • SGI通常用于多核之间的通信。GIC-V2最多支持16个SGI,硬件中断号范围为0~15。SGI通常在Linux内核中被用作处理器之间的中断 (Inter-Processor Interrupt, IPI),并会送达到系统指定的CPU上
  • PPI是每个处理器内核私有的中断。GIC-V2 最多支持16个PPI中断,硬件中断号范围为16~31。PPI通常会送达到指定的CPU 上,应用场景有CPU本地定时器(local timer)。
  • SPI是公用的外设中断。GIC-V2最多可以支持988个外设中断,硬件中断号范围为32~1019。
  • SGI和PPI是每个CPU私有的中断,而SPI是所有CPU内核共享的。

外设中断可以支持两种中断触发方式

  • 边沿触发(edge-triggered):当中断源产生一个上升沿或者下降沿时,触发一个中断
  • 电平触发(level-sensitive):当中断信号线产生一个高电平或者低电平时,触发一个 中断

GIC主要由两部分组成,分别是仲裁单元(distributor)和CPU接口模块。仲裁单元为每一个中断源维护一个状态机,支持的状态有 inactive pending、active和active and pending。GIC检测中断的流程如下:

  1. 当GIC检测到一个中断发生时,会将该中断标记为pending状态

  2. 对于处于pending状态的中断,仲裁单元会确定目标CPU,将中断请求发送到这个CPU

  3. 对于每个CPU,仲裁单元会从众多处于pending状态的中断中选择一个优先级最高的中断,发送到目标CPU的CPU接口模块上

  4. CPU接口模块会决定这个中断是否可以发送给CPU。如果该中断的优先级满足要求,GIC会发送一个中断请求信号给该CPU

  5. 当一个CPU进入中断异常后,会读取GICC_IAR来响应该中断(一般由Linux内核的中断处理程序来读寄存器)。寄存器会返回硬件中断号(hardware interrupt ID),对于SGI来说,返回源CPU的ID(source processor ID)。当GIC感知到软件读取了该寄存器后,又分为如下情况:

    • 如果该中断处于pending状态,那么状态将变成active
    • 如果该中断又重新产生,那么pending将状态变成active and pending状态
    • 如果该中断处于active状态,将变成active and pending状态
  6. 当处理器完成中断服务,必须发送一个完成信号结束中断(End Of Interrupt, EOI)给GIC

2. 硬件中断号和 Linux 中断号的映射

在Linux中,注册中断接口函数request_irq()、request_threaded_irq() 使用Linux内核软件中断号(俗称软件中断号或IRQ号),而不是硬件中断号。接下来,以QEMU虚拟机的串口 0设备(它在主板上的序号为1,硬件中断号为33)为例,介绍硬件中断号是如何和Linux内核的IRQ号映射的。

  • 首先介绍下前置知识

    • ARM64平台的设备描述基本上采用设备树(Device Tree)模式来描述硬件设备。QEMU虚拟机的设备树的描述脚本并没有实现在内核代码中,而实现在QEMU代码里。因此,可以通过DTC命令反编译出设备树脚本(Device Tree Script, DTS),反编译 DTS时,与串口中断相关的描述如下:

      pl011@9000000 {clock-names = "uartclk\Oapb_pclk";clocks = < 0x8000 0x8000 >;
      // interrupts 域描述相关的属性:
      // 中断类型,共享外设中断(GIC_SPI)在设备树中用0来表示,私有外设中断(GIC_PPI)在设备树中用1来表示,这里是0x00
      // 中断 ID,这里是0x01
      // 触发类型,(即IRQ_TYPE_LEVEL_HIGH,高电平触发)
      interrupts = < 0x00 0x01 0x04 >;reg = < 0x00 0x9000000 0x00 0x1000 >;
      // "arm,pl011\0arm,primecell" 外设的兼容字符串,用于和驱动程序进行匹配工作
      compatible = "arm,pl011\0arm,primecell";
      };
      
    • 一个中断控制器用一个irq_domain数据结构来抽象描述,irq_domain数据结构的定义如下:

      // 一个中断控制器用一个 irq_domain 数据结构来抽象描述
      struct irq_domain {// 用于将 irq_domain 连接到全局链表 irq_domain_list 中struct list_head link;// irq_domain 的名称const char *name;// irq_domain 映射操作使用的方法集合const struct irq_domain_ops *ops;...// 该 irq_domain 支持中断数量的最大值irq_hw_number_t hwirq_max;unsigned int revmap_direct_max_irq;// 线性映射的大小unsigned int revmap_size;// 基数树映射的根节点struct radix_tree_root revmap_tree;struct mutex revmap_tree_mutex;// 线性映射用到的查找表unsigned int linear_revmap[];
      };
      
  • 系统初始化时会查找DTS中定义的中断控制器,计算GIC最多支持的中断源的个数,GIC-V2规定最多支持1020个中断源,在SoC设计阶段就确定ARM SoC可以支持多少个中断源了,然后,调用irq_domain_create_linear()->__irq_domain_add()函数注册一个irq_domain数据结构:

    struct irq_domain *__irq_domain_add(struct fwnode_handle *fwnode, int size,irq_hw_number_t hwirq_max, int direct_max,const struct irq_domain_ops *ops,void *host_data)
    {...// 注册一个 irq_domain 数据结构domain = kzalloc_node(sizeof(*domain) + (sizeof(unsigned int) * size),GFP_KERNEL, of_node_to_nid(of_node));// 初始化 irq_domain 数据结构...// 把 irq_domain 数据结构加入全局的链表 irq_domain_list 中list_add(&domain->link, &irq_domain_list);...
    }
    
  • 回到系统枚举阶段的中断号映射过程,在of_amba_device_create()函数中,irq_of_parse_and_map()函数负责把硬件中断号映射到Linux内核的IRQ号

    // 把硬件中断号映射到 Linux 内核的 IRQ 号
    unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
    {struct of_phandle_args oirq;if (of_irq_parse_one(dev, index, &oirq))return 0;return irq_create_of_mapping(&oirq);
    }
    
  • irq_of_parse_and_map()->of_irq_parse_one():主要用于解析DTS文件中设备定义的属性,如reg、interrupts等, 最后把DTS中的interrupts的值存放在oirq->args[]数组中

  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()

    unsigned int irq_create_fwspec_mapping(struct irq_fwspec *fwspec)
    {...// 查找外设所属的中断控制器的 irq_domainif (fwspec->fwnode) {domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_WIRED);if (!domain)domain = irq_find_matching_fwspec(fwspec, DOMAIN_BUS_ANY);} else {domain = irq_default_domain;}if (!domain) {pr_warn("no irq domain found for %s !\n",of_node_full_name(to_of_node(fwspec->fwnode)));return 0;}// 进行硬件中断号的转换// hwirq 存储着这个硬件中断号,type存储该外设的中断类型if (irq_domain_translate(domain, fwspec, &hwirq, &type))return 0;...// 如果这个硬件中断号已经映射过了,那么 irq_find_mapping() 函数可以找到映射后的中断号,在此情境下,该硬件中断号还没有映射virq = irq_find_mapping(domain, hwirq);if (virq) {...}if (irq_domain_is_hierarchy(domain)) {// 映射的核心函数virq = irq_domain_alloc_irqs(domain, 1, NUMA_NO_NODE, fwspec);if (virq <= 0)return 0;} else {/* Create mapping */virq = irq_create_mapping(domain, hwirq);if (!virq)return virq;}...return virq;
    }
    
  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()->irq_domain_alloc_irqs()->__irq_domain_alloc_irqs->irq_domain_alloc_descs()->__irq_alloc_descs()

    int __ref
    __irq_alloc_descs(int irq, unsigned int from, unsigned int cnt, int node,struct module *owner, const struct irq_affinity_desc *affinity)
    {...// 在 allocated_irqs 位图中查找第一个包含连续 cnt 个 0 的位图区域start = bitmap_find_next_zero_area(allocated_irqs, IRQ_BITMAP_BITS,from, cnt, 0);...// 分配 irq_desc 数据结构ret = alloc_descs(start, cnt, node, affinity, owner);
    ...
    }
    

    irq_desc数据结构:

    struct irq_desc {...struct irq_data		irq_data;...
    } ____cacheline_internodealigned_in_smp;
    

    irq_data数据结构:

    struct irq_data {...// 软件中断号unsigned int		irq;// 硬件中断号unsigned long		hwirq;...
    };
    
  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()->irq_domain_alloc_irqs()->__irq_domain_alloc_irqs()->irq_domain_alloc_descs()函数返回 allocated_irqs 位图中第一个空闲位,这是软件中断号

  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()->irq_domain_alloc_irqs()->__irq_domain_alloc_irqs()->irq_domain_alloc_irqs_hierarchy()->gic_irq_domain_alloc()

    static int gic_irq_domain_alloc(struct irq_domain *domain, unsigned int virq,unsigned int nr_irqs, void *arg)
    {...// 解析出硬件中断号并存放在 hwirq 中ret = gic_irq_domain_translate(domain, fwspec, &hwirq, &type);...for (i = 0; i < nr_irqs; i++) {// 映射工作ret = gic_irq_domain_map(domain, virq + i, hwirq + i);if (ret)return ret;}return 0;
    }
    
  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()->irq_domain_alloc_irqs()->__irq_domain_alloc_irqs()->irq_domain_alloc_irqs_hierarchy()->gic_irq_domain_alloc()->gic_irq_domain_map()->irq_domain_set_info()->irq_domain_set_hwirq_and_chip()通过IRQ号获取irq_data数据结构,并把硬件中断号hwirq设置到irq_data数据结构中的hwirq成员中,就完成了硬件中断号到软件中断号的映射。

  • irq_of_parse_and_map()->irq_create_of_mapping->irq_create_fwspec_mapping()->irq_domain_alloc_irqs()->__irq_domain_alloc_irqs()->irq_domain_alloc_irqs_hierarchy()->gic_irq_domain_alloc()->gic_irq_domain_map()->irq_domain_set_info()->__irq_set_handler():设置中断描述符 desc->handle_irq 的回调函数

  • 综上所述,硬件中断号和软件中断号的映射过程如下图所示:

    在这里插入图片描述

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

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

相关文章

规划控制如何兼顾安全与舒适性

规划控制如何兼顾安全与舒适性 附赠自动驾驶学习资料和量产经验&#xff1a;链接 **导读&#xff1a;**自动驾驶技术研发对于“安全第一”的追求是毋庸置疑的&#xff0c;但是这中间可能就忽视了舒适性。 因此&#xff0c;今天我想给大家分享的是&#xff0c;自动驾驶研发如何…

《Java面试自救指南》(专题一)操作系统

文章目录 力推操作系统的三门神课操作系统的作用和功能线程、进程和协程的区别并行与并发的区别什么是文件描述符操作系统内核态和用户态的区别用户态切换到内核态的方式大内核和微内核的区别用户级线程和内核级线程的区别线程的七态模型进程调度算法有哪些进程间通信的七种方式…

zookeeper如何管理客户端与服务端之间的链接?(zookeeper sessions)

zookeeper客户端与服务端之间的链接用zookeeper session表示。 zookeeper session有三个状态&#xff1a; CONNECTING, ASSOCIATING, CONNECTED, CONNECTEDREADONLY, CLOSED, AUTH_FAILED, NOT_CONNECTED&#xff08;start时的状态&#xff09; 1、CONNECTING 。 表明客户…

【Linux2】Linux的权限

思维导图 学习内容 在介绍完一些基本指令后&#xff0c;我们需要进行对权限以后一个全新的认识&#xff0c;比如文件的权限、目录的权限等等…… 学习内容 通过上面的学习目标&#xff0c;我们可以列出要学习的内容&#xff1a; shell命令以及运行原理Linux权限的概念Linux权…

【Blockchain】GameFi | NFT

Blockchain GameFiGameFi顶级项目TheSandbox&#xff1a;Decentraland&#xff1a;Axie Infinity&#xff1a; NFTNFT是如何工作的同质化和非同质化区块链协议NFT铸币 GameFi GameFi是游戏和金融的组合&#xff0c;它涉及区块链游戏&#xff0c;对玩家提供经济激励&#xff0c…

失物招领(源码+文档)

失物招领&#xff08;小程序、ios、安卓都可部署&#xff09; 文件包含内容程序简要说明含有功能项目截图客户端首页注册界面发布动态我的详细登录修改资料发布动态 管理端后台登录用户管理分类管理内容管理 文件包含内容 1、搭建视频 2、流程图 3、开题报告 4、数据库 5、参考…

施耐德 PLC 控制系统 产品 + 软件总体介绍 2020

参考 2020.7 官方说明视频&#xff1a;https://www.bilibili.com/video/BV1Mi4y1G7Qc/ 总体说明 施耐德作为工业控制界巨头&#xff08;公认的几大巨头&#xff1a;西门子、AB、施耐德&#xff09;&#xff0c;PLC 控制器产品线很庞大&#xff0c;涵盖了高中低的完整产品线&…

webpack打包模块

webpack打包模块 一.webpack简介二.Webpack 修改入口和出口三.Webpack 自动生成 html 文件四.Webpack-打包 css 代码五.优化-提取 css 代码六.优化压缩过程七.Webpack-打包图片 一.webpack简介 1.Webpack 是一个静态模块打包工具&#xff0c;从入口构建依赖图&#xff0c;打包…

吴恩达2022机器学习专项课程(一) 4.4 学习率

问题预览/关键词 学习率太小有什么影响&#xff1f;学习率太大有什么影响&#xff1f;如果成本函数达到局部最小值&#xff0c;使用梯度下降还能继续最小化吗&#xff1f;为什么学习率固定&#xff0c;而最小化成本函数的步幅却越来越缓&#xff1f;如何选择合适的学习率&…

算法学习——LeetCode力扣图论篇3(127. 单词接龙、463. 岛屿的周长、684. 冗余连接、685. 冗余连接 II)

算法学习——LeetCode力扣图论篇3 127. 单词接龙 127. 单词接龙 - 力扣&#xff08;LeetCode&#xff09; 描述 字典 wordList 中从单词 beginWord 和 endWord 的 转换序列 是一个按下述规格形成的序列 beginWord -> s1 -> s2 -> … -> sk&#xff1a; 每一对相…

【苹果MAC】苹果电脑 LOGI罗技鼠标设置左右切换全屏页面快捷键

首先键盘设置->键盘快捷键 调度中心 设置 f1 f2 为移动一个空间&#xff08;就可以快捷移动了&#xff09; 想要鼠标直接控制&#xff0c;就需要下载官方驱动&#xff0c;来设置按键快捷键&#xff0c;触发 F1 F2 安装 LOGI OPTIONS Logi Options 是一款功能强大且便于使用…

Spring Boot单元测试全指南:使用Mockito和AssertJ

&#x1f31f; 前言 欢迎来到我的技术小宇宙&#xff01;&#x1f30c; 这里不仅是我记录技术点滴的后花园&#xff0c;也是我分享学习心得和项目经验的乐园。&#x1f4da; 无论你是技术小白还是资深大牛&#xff0c;这里总有一些内容能触动你的好奇心。&#x1f50d; &#x…

Verilog语法回顾--用户定义原语

目录 用户定义原语 UDP定义 UDP状态表 状态表符号 组合UDP 电平敏感UDP 沿敏感时序UDP 参考《Verilog 编程艺术》魏家明著 用户定义原语 用户定义原语&#xff08;User-defined primitive&#xff0c;UDP&#xff09;是一种模拟硬件技术&#xff0c;可以通过设计新的原…

人工智能产业应用--具身智能

五、下一个浪潮 (一) 跳出缸中脑——虚实结合 在探索人工智能的边界时&#xff0c;“跳出缸中脑——虚实结合”这一概念提出了一个引人深思的视角&#xff0c;尤其是在具身智能的领域。具身智能是一种思想&#xff0c;强调智能体通过与其环境的直接物理互动来实现智能行为。然…

QT-左框选项卡软件界面框架

QT-左框选项卡软件界面框架 一、演示效果二、关键程序三、下载链接 一、演示效果 二、关键程序 #include <QTextBrowser> #include <QLabel> #include <QPushButton> #include <QSpacerItem> #include <QToolButton> #include <QDebug> #i…

【MyBatis】MyBatis的介绍和基本使用

目录 一、数据库操作框架的历程 1.1 JDBC 1.2 DBUtils 1.3 Hibernate 1.4 Spring JDBC&#xff1a;JDBCTemplate 1.5 Spring Data JPA 二、什么是MyBatis&#xff1f; 2.1 传统JDBC与MyBatis相比的弊病 2.2 MyBatis中的组件 2.3 MyBatis的体系结构 三、快速搭建MyBa…

Linux的中间件

我们先补充点关于awk的内容 awk的用法其实很广。 $0 表示整条记录 变量&#xff1a; NF 一行中有多少个字段&#xff08;表示字段数&#xff09; NR &#xff1a; 代表当前记录的序号&#xff0c;从1开始计数。每读取一条记录&#xff0c;NR的值就会自动增加1。&#xff08;…

鸿蒙TypeScript入门学习第5天:【TypeScript 运算符】

1、TypeScript 运算符 运算符用于执行程序代码运算&#xff0c;会针对一个以上操作数项目来进行运算。 考虑以下计算&#xff1a; 7 5 12复制以上实例中 7、5 和 12 是操作数。 运算符 用于加值。 运算符 用于赋值。 TypeScript 主要包含以下几种运算&#xff1a; 算…

NEO 学习之 MLE(最大似然估计)

文章目录 简单题目MLE 在不同的分布的运用正态分布指数分布均匀分布泊松分布 如何理解 最大似然估计&#xff1f; 就是我们先取出一堆样本&#xff0c;得到一个L( θ \theta θ) 函数&#xff0c;然后的话&#xff0c;这个是关于 θ \theta θ 的一个函数&#xff0c;那么由于存…

C++的入门学习

✨✨✨学习的道路很枯燥&#xff0c;希望我们能并肩走下来! 文章目录 目录 文章目录 前言 一、C关键字(C98) 二、命名空间 2.1 引入 ​编辑2.2 命名空间定义 2.3 命名空间的使用 三. C输入&输出 四.缺省参数 4.1 缺省参数概念 4.2 缺省参数分类 1.全缺省参数 2…