常见泽1 发表于 2025-1-17 18:15

《Linux内核深度解析》第4章 异常与中断学习

<p>在ARM64和MIPS这些精简指令集计算机(RISC)体系结构中,中断系统调用和其他打断程序正常执行流的事件统称为异常,这是广义的异常。</p>

<p >异常级别,4个异常级别0-3</p>

<p >&nbsp;</p>

<p > &nbsp;</p>

<p >异常分为同步异常和异步异常</p>

<p >同步异常是试图执行指令时产生的异常,或是作为指令的执行结果生成的异常。</p>

<p >同步异常包括:</p>

<p >(1)系统调用,例如异常级别0使用SVC指令陷入异常级别1</p>

<p >(2)数据中止,即访问数据时的页错误异常,虚拟地址没有映射到物理地址,或者没有写权限</p>

<p >(3)指令中止,即取指令时的页错误异常,虚拟地址没有映射到物理地址,或没有执行权限</p>

<p >(4)栈指针或指令地址没有对齐</p>

<p >(5)没有定义的指令</p>

<p >(6)调试异常</p>

<p >&nbsp;</p>

<p >异步异常,不是由正在执行的指令生成的,和正在执行的指令没有关联</p>

<ol>
        <li >中断</li>
        <li >快速中断</li>
        <li >系统错误</li>
</ol>

<p >&nbsp;</p>

<p >异步异常感觉更像是MCU里面的一些异常,中断类似于STM32的NVIC中断</p>

<p >&nbsp;</p>

<p >系统错误也是类似于STM32的一些HARDFAULT、总线fault或者NMI等</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >LINUX的异常和MCU也是一样,有一些异常向量表。</p>

<p >&nbsp;</p>

<div style="text-align: center;"></div>

<p>&nbsp;</p>

<p >&nbsp;</p>

<p >异常处理的流程</p>

<ol>
        <li >把当前的处理器状态保存在寄存器SPSR_EL1程序状态寄存器中</li>
        <li >把返回地址保存在寄存器ELR_EL1异常链接寄存器中</li>
        <li >把处理器状态的DAIF这4个异常掩码位都设置为1.禁止这4种异常</li>
        <li >如果是同步异常或系统错误异常,把生成异常的原因保存在寄存器ESR_EL1异常症状寄存器中</li>
        <li >如果是同步异常,把错误地址保存在寄存器FAR_EL1错误地址寄存器中</li>
        <li >根据向量基准地址寄存器异常类型和生成异常的异常级别额计算出异常向量的虚拟地址,执行异常向量。</li>
</ol>

<p >&nbsp;</p>

<p >这段异常处理流程,其实和CORTEX M3的异常处理流程也是有类似的&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >LINUX中断处理</p>

<p >第一层处理函数是中断描述符的成员handle_irq()</p>

<p >第二层处理函数是设备驱动程序注册的处理函数。中断描述符由一个中断处理链表(irq_desc.action),每个中断处理描述符(irq_action)保存设备驱动程序注册的处理函数。因为多个设备可以共享同一个硬件中断号,所以中断处理链表可能挂载多个中断处理描述符。</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >源码在kerne/irq/irqdesc.c函数文件里</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >用来存用户注册的中断处理函数,一个中断可以有多个处理函数 ,当一个中断有多个处理函数,说明这个是共享中断,所谓共享中断就是一个中断的来源有很多,这些来源共享同一个引脚。所以在irq_desc结构体中的action成员是个链表,以action为表头,若是一个以上的链表就是共享中断</p>

<pre>
<code class="language-cpp">struct irq_desc {
    struct irq_data      irq_data;
    unsigned int __percpu    *kstat_irqs;------IRQ的统计信息
    irq_flow_handler_t    handle_irq;--------(1)
    struct irqaction    *action; -----------(2)
    unsigned int      status_use_accessors;-----中断描述符的状态,参考IRQ_xxxx
    unsigned int      core_internal_state__do_not_mess_with_it;----(3)
    unsigned int      depth;----------(4)
    unsigned int      wake_depth;--------(5)
    unsigned int      irq_count;---------(6)
    unsigned long      last_unhandled;
    unsigned int      irqs_unhandled;
    raw_spinlock_t      lock;-----------(7)
    struct cpumask      *percpu_enabled;-------(8)
#ifdef CONFIG_SMP
    const struct cpumask    *affinity_hint;----和irq affinity相关,后续单独文档描述
    struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
    cpumask_var_t      pending_mask;
#endif
#endif
    unsigned long      threads_oneshot; -----(9)
    atomic_t      threads_active;
    wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PROC_FS
    struct proc_dir_entry    *dir;--------该IRQ对应的proc接口
#endif
    int            parent_irq;
    struct module      *owner;
    const char      *name;
} ____cacheline_internodealigned_in_smp
</code></pre>

<p>&nbsp;</p>

<p>在Linux内核中,中断处理流程通常涉及多个函数,其中handle_irq_event_percpu是处理中断事件的关键函数之一。该函数通过调用在request_irq中注册的中断处理程序(ISR)来处理中断事件。具体来说,当CPU接收到一个中断时,会调用handle_irq_event函数,该函数进一步调用handle_irq_event_percpu来处理每个CPU上的中断事件&zwnj;</p>

<p > &nbsp;</p>

<p >&nbsp;</p>

<p >&nbsp;</p>

<p >中断描述符的初始化的接口kerne/irq/irqdesc.c函数文件里,在这个文件里有两个Irq的初始化,越往下看越复杂,完全超出了我的能力了,不做内核开发方向的,看这些真的很费劲很难。</p>

<p >&nbsp;</p>

<p > &nbsp;</p>

<p > &nbsp;</p>

hellokitty_bean 发表于 2025-1-17 21:19

<p>楼主让人敬佩呀。。。。。。。。。。。。。。</p>

<p>不做内核开发的方向,还能沉下心来研究。。。。。。。<img height="52" src="https://bbs.eeworld.com.cn/static/editor/plugins/hkemoji/sticker/facebook/pleased.gif" width="48" /></p>

常见泽1 发表于 2025-1-18 17:38

hellokitty_bean 发表于 2025-1-17 21:19
楼主让人敬佩呀。。。。。。。。。。。。。。

不做内核开发的方向,还能沉下心来研究。。。。。。。

<p>太难了&nbsp; 基本放弃了&nbsp; 完全搞不明白</p>
页: [1]
查看完整版本: 《Linux内核深度解析》第4章 异常与中断学习