虚拟主机怎么发布网站,wordpress自动更新,社交媒体营销策略有哪些,在建工程背景Kernel版本#xff1a;4.14ARM64处理器#xff0c;Contex-A53#xff0c;双核使用工具#xff1a;Source Insight 3.5#xff0c; Visio1. 概述《Linux中断子系统#xff08;一#xff09;-中断控制器及驱动分析》讲到了底层硬件GIC驱动#xff0c;以及Arch-Specif… 背景Kernel版本4.14ARM64处理器Contex-A53双核使用工具Source Insight 3.5 Visio1. 概述《Linux中断子系统一-中断控制器及驱动分析》讲到了底层硬件GIC驱动以及Arch-Specific的中断代码本文将研究下通用的中断处理的过程属于硬件无关层。当然我还是建议你看一下上篇文章。这篇文章会解答两个问题用户是怎么使用中断的中断注册外设触发中断信号时最终是怎么调用到中断handler的中断处理2. 数据结构分析先来看一下总的数据结构核心是围绕着struct irq_desc来展开Linux内核的中断处理围绕着中断描述符结构struct irq_desc展开内核提供了两种中断描述符组织形式打开CONFIG_SPARSE_IRQ宏中断编号不连续中断描述符以radix-tree来组织用户在初始化时进行动态分配然后再插入radix-tree中关闭CONFIG_SPARSE_IRQ宏中断编号连续中断描述符以数组的形式组织并且已经分配好不管哪种形式都可以通过linux irq号来找到对应的中断描述符图的左侧灰色部分主要在中断控制器驱动中进行初始化设置包括各个结构中函数指针的指向等其中struct irq_chip用于对中断控制器的硬件操作struct irq_domain与中断控制器对应完成的工作是硬件中断号到Linux irq的映射图的上侧灰色部分中断描述符的创建这里指CONFIG_SPARSE_IRQ主要在获取设备中断信息的过程中完成的从而让设备树中的中断能与具体的中断描述符irq_desc匹配图中剩余部分在设备申请注册中断的过程中进行设置比如struct irqaction中handler的设置这个用于指向我们设备驱动程序中的中断处理函数了中断的处理主要有以下几个功能模块硬件中断号到Linux irq中断号的映射并创建好irq_desc中断描述符中断注册时先获取设备的中断号根据中断号找到对应的irq_desc并将设备的中断处理函数添加到irq_desc中设备触发中断信号时根据硬件中断号得到Linux irq中断号找到对应的irq_desc最终调用到设备的中断处理函数上述的描述比较简单更详细的过程往下看吧。3. 流程分析3.1 中断注册这一次让我们以问题的方式来展开先来让我们回答第一个问题用户是怎么使用中断的熟悉设备驱动的同学应该都清楚经常会在驱动程序中调用request_irq()接口或者request_threaded_irq()接口来注册设备的中断处理函数request_irq()/request_threaded_irq接口中都需要用到irq也就是中断号那么这个中断号是从哪里来的呢它是Linux irq它又是如何映射到具体的硬件设备的中断号的呢先来看第二个问题设备硬件中断号到Linux irq中断号的映射硬件设备的中断信息都在设备树device tree中进行了描述在系统启动过程中这些信息都已经加载到内存中并得到了解析驱动中通常会使用platform_get_irq或irq_of_parse_and_map接口去根据设备树的信息去创建映射关系硬件中断号到linux irq中断号映射《Linux中断子系统一-中断控制器及驱动分析》提到过struct irq_domain用于完成映射工作因此在irq_create_fwspec_mapping接口中会先去找到匹配的irq domain再去回调该irq domain中的函数集通常irq domain都是在中断控制器驱动中初始化的以ARM GICv2为例最终回调到gic_irq_domain_hierarchy_ops中的函数如果已经创建好了映射那么可以直接进行返回linux irq中断号了否则的话需要irq_domain_alloc_irqs来创建映射关系irq_domain_alloc_irqs完成两个工作针对linux irq中断号创建一个irq_desc中断描述符调用domain-ops-alloc函数来完成映射在ARM GICv2驱动中对应gic_irq_domain_alloc函数这个函数很关键所以下文介绍一下gic_irq_domain_alloc函数如下gic_irq_domain_translate负责解析出设备树中描述的中断号和中断触发类型边缘触发、电平触发等gic_irq_domain_map将硬件中断号和linux中断号绑定到一个结构中也就完成了映射此外还绑定了irq_desc结构中的其他字段最重要的是设置了irq_desc-handle_irq的函数指针这个最终是中断响应时往上执行的入口这个是关键下文讲述中断处理过程时还会提到根据硬件中断号的范围设置irq_desc-handle_irq的指针共享中断入口为handle_fasteoi_irq私有中断入口为handle_percpu_devid_irq上述函数执行完成后完成了两大工作硬件中断号与Linux中断号完成映射并为Linux中断号创建了irq_desc中断描述符数据结构的绑定及初始化关键的地方是设置了中断处理往上执行的入口再看第一个问题中断是怎么来注册的设备驱动中获取到了irq中断号后通常就会采用request_irq/request_threaded_irq来注册中断其中request_irq用于注册普通处理的中断request_threaded_irq用于注册线程化处理的中断在讲具体的注册流程前先看一下主要的中断标志位#define IRQF_SHARED 0x00000080 //多个设备共享一个中断号需要外设硬件支持
#define IRQF_PROBE_SHARED 0x00000100 //中断处理程序允许sharing mismatch发生
#define __IRQF_TIMER 0x00000200 //时钟中断
#define IRQF_PERCPU 0x00000400 //属于特定CPU的中断
#define IRQF_NOBALANCING 0x00000800 //禁止在CPU之间进行中断均衡处理
#define IRQF_IRQPOLL 0x00001000 //中断被用作轮训
#define IRQF_ONESHOT 0x00002000 //一次性触发的中断不能嵌套1在硬件中断处理完成后才能打开中断2在中断线程化中保持关闭状态直到该中断源上的所有thread_fn函数都执行完
#define IRQF_NO_SUSPEND 0x00004000 //系统休眠唤醒操作中不关闭该中断
#define IRQF_FORCE_RESUME 0x00008000 //系统唤醒过程中必须强制打开该中断
#define IRQF_NO_THREAD 0x00010000 //禁止中断线程化
#define IRQF_EARLY_RESUME 0x00020000 //系统唤醒过程中在syscore阶段resume而不用等到设备resume阶段
#define IRQF_COND_SUSPEND 0x00040000 //与NO_SUSPEND的用户共享中断时执行本设备的中断处理函数
request_irq也是调用request_threaded_irq只是在传参的时候线程处理函数thread_fn函数设置成NULL由于在硬件中断号和Linux中断号完成映射后irq_desc已经创建好可以通过irq_to_desc接口去获取对应的irq_desc创建irqaction并初始化该结构体中的各个字段其中包括传入的中断处理函数赋值给对应的字段__setup_irq用于完成中断的相关设置包括中断线程化的处理中断线程化用于减少系统关中断的时间增强系统的实时性ARM64默认开启了CONFIG_IRQ_FORCED_THREADING引导参数传入threadirqs时则除了IRQF_NO_THREAD外的中断其他的都将强制线程化处理中断线程化会为每个中断都创建一个内核线程如果中断进行共享对应irqaction将连接成链表每个irqaction都有thread_mask位图字段当所有共享中断都处理完成后才能unmask中断解除中断屏蔽3.2 中断处理当完成中断的注册后所有结构的组织关系都已经建立好剩下的工作就是当信号来临时进行中断的处理工作。来回顾一下《Linux中断子系统一-中断控制器及驱动分析》中的Arch-specific处理流程中断收到之后首先会跳转到异常向量表的入口处进而逐级进行回调处理最终调用到generic_handle_irq来进行中断处理。generic_handle_irq处理如下图generic_handle_irq函数最终会调用到desc-handle_irq()这个也就是对应到上文中在建立映射关系的过程中调用irq_domain_set_info函数设置好了函数指针也就是handle_fasteoi_irq和handle_percpu_devid_irqhandle_fasteoi_irq处理共享中断并且遍历irqaction链表逐个调用action-handler()函数这个函数正是设备驱动程序调用request_irq/request_threaded_irq接口注册的中断处理函数此外如果中断线程化处理的话还会调用__irq_wake_thread()唤醒内核线程handle_percpu_devid_irq处理per-CPU中断处理在这个过程中会分别调用中断控制器的处理函数进行硬件操作该函数调用action-handler()来进行中断处理来看看中断线程化处理后的唤醒流程吧__handle_irq_event_percpu-__irq_wake_thread__handle_irq_event_percpu-__irq_wake_thread将唤醒irq_thread中断内核线程irq_thread内核线程将根据是否为强制中断线程化对函数指针handler_fn进行初始化以便后续进行调用irq_thread内核线程将while(!irq_wait_for_interrupt)循环进行中断的处理当满足条件时执行handler_fn在该函数中最终调用action-thread_fn也就是完成了中断的处理irq_wait_for_interrupt函数将会判断中断线程的唤醒条件如果满足了则将当前任务设置成TASK_RUNNING状态并返回0这样就能执行中断的处理否则就调用schedule()进行调度让出CPU并将任务设置成TASK_INTERRUPTIBLE可中断睡眠状态3.3 总结中断的处理总体来说可以分为两部分来看从上到下围绕irq_desc中断描述符建立好连接关系这个过程就包括中断源信息的解析设备树硬件中断号到Linux中断号的映射关系、irq_desc结构的分配及初始化内部各个结构的组织关系、中断的注册填充irq_desc结构包括handler处理函数等总而言之就是完成静态关系创建为中断处理做好准备从下到上当外设触发中断信号时中断控制器接收到信号并发送到处理器此时处理器进行异常模式切换并逐步从处理器架构相关代码逐级回调。如果涉及到中断线程化则还需要进行中断内核线程的唤醒操作最终完成中断处理函数的执行。 推荐阅读 专辑|Linux文章汇总 专辑|程序人生 专辑|C语言嵌入式Linux微信扫描二维码关注我的公众号