中断产生流程

news2024/12/23 16:39:23

 中断产生流程

中断向量表

entry.S (arch\arm64\kernel)

ENTRY(vectors)
    kernel_ventry    1, sync_invalid            // Synchronous EL1t
    kernel_ventry    1, irq_invalid            // IRQ EL1t
    kernel_ventry    1, fiq_invalid            // FIQ EL1t
    kernel_ventry    1, error_invalid        // Error EL1t

    kernel_ventry    1, sync                // Synchronous EL1h
    kernel_ventry    1, irq                // IRQ EL1h
    kernel_ventry    1, fiq_invalid            // FIQ EL1h
    kernel_ventry    1, error_invalid        // Error EL1h

    kernel_ventry    0, sync                // Synchronous 64-bit EL0
    kernel_ventry    0, irq                // IRQ 64-bit EL0
    kernel_ventry    0, fiq_invalid            // FIQ 64-bit EL0
    kernel_ventry    0, error_invalid        // Error 64-bit EL0

#ifdef CONFIG_COMPAT
    kernel_ventry    0, sync_compat, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_compat, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid_compat, 32    // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid_compat, 32    // Error 32-bit EL0
#else
    kernel_ventry    0, sync_invalid, 32        // Synchronous 32-bit EL0
    kernel_ventry    0, irq_invalid, 32        // IRQ 32-bit EL0
    kernel_ventry    0, fiq_invalid, 32        // FIQ 32-bit EL0
    kernel_ventry    0, error_invalid, 32        // Error 32-bit EL0
#endif
END(vectors)

以el1_irq为例

el1_irq:
    kernel_entry 1
    enable_dbg
#ifdef CONFIG_TRACE_IRQFLAGS
    bl    trace_hardirqs_off
#endif

    irq_handler

#ifdef CONFIG_PREEMPT
    ldr    w24, [tsk, #TSK_TI_PREEMPT]    // get preempt count
    cbnz    w24, 1f                // preempt count != 0
    ldr    x0, [tsk, #TSK_TI_FLAGS]    // get flags
    tbz    x0, #TIF_NEED_RESCHED, 1f    // needs rescheduling?
    bl    el1_preempt
1:
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
    bl    trace_hardirqs_on
#endif
    kernel_exit 1
ENDPROC(el1_irq)

从上kernel_entry 保存现场, irq_handler是中断处理函数,kernel_exit是恢复现场

中间CONFIG_PREEMPT宏包含一段抢占调度的代码,下面介绍一下irq_handler函数

    .macro    irq_handler
    ldr_l    x1, handle_arch_irq
    mov    x0, sp
    irq_stack_entry  //栈指针切换,切换到中断栈空间
    blr    x1              //跳到handle_arch_irq函数执行
    irq_stack_exit  //栈指针切换,退出中断栈空间
    .endm

handle_arch_irq是通过set_handle_irq赋值的

路径:entry.S (kernel4.14\arch\arm64\kernel)

void __init set_handle_irq(void (*handle_irq)(struct pt_regs *))
{
    if (handle_arch_irq)
        return;

    handle_arch_irq = handle_irq;
}

此set_handle_irq函数是在gic_init_bases函数中调用的 set_handle_irq(gic_handle_irq);

gic_handle_irq函数路径:Irq-gic-v3.c (kernel4.14\drivers\irqchip)

static asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqnr;

    do {
        irqnr = gic_read_iar();//读取硬件中断号

        if (likely(irqnr > 15 && irqnr < 1020) || irqnr >= 8192) {
            int err;

            if (static_key_true(&supports_deactivate))
                gic_write_eoir(irqnr);
            else
                isb();

            err = handle_domain_irq(gic_data.domain, irqnr, regs);
            if (err) {
                WARN_ONCE(true, "Unexpected interrupt received!\n");
                if (static_key_true(&supports_deactivate)) {
                    if (irqnr < 8192)
                        gic_write_dir(irqnr);
                } else {
                    gic_write_eoir(irqnr);
                }
            }
            continue;
        }
        if (irqnr < 16) {//主要用于核间通信的中断
            gic_write_eoir(irqnr);
            if (static_key_true(&supports_deactivate))
                gic_write_dir(irqnr);
#ifdef CONFIG_SMP
            /*
             * Unlike GICv2, we don't need an smp_rmb() here.
             * The control dependency from gic_read_iar to
             * the ISB in gic_write_eoir is enough to ensure
             * that any shared data read by handle_IPI will
             * be read after the ACK.
             */
            handle_IPI(irqnr, regs);
#else
            WARN_ONCE(true, "Unexpected SGI received!\n");
#endif
            continue;
        }
    } while (irqnr != ICC_IAR1_EL1_SPURIOUS);
}

static inline int handle_domain_irq(struct irq_domain *domain,
                    unsigned int hwirq, struct pt_regs *regs)
{
    return __handle_domain_irq(domain, hwirq, true, regs);
}

int __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq,
            bool lookup, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);
    unsigned int irq = hwirq;
    int ret = 0;

    irq_enter();//进入中断上下文

#ifdef CONFIG_IRQ_DOMAIN
    if (lookup)
        irq = irq_find_mapping(domain, hwirq);//根据硬件中断号找到对应的软件中断号

#endif

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(!irq || irq >= nr_irqs)) {
        ack_bad_irq(irq);
        ret = -EINVAL;
    } else {
        generic_handle_irq(irq);//进入一个中断的处理函数
    }

    irq_exit();//退出中断上下文
    set_irq_regs(old_regs);
    return ret;
}‘

generic_handle_irq函数如下:

int generic_handle_irq(unsigned int irq)
{
    struct irq_desc *desc = irq_to_desc(irq);

    if (!desc)
        return -EINVAL;
    generic_handle_irq_desc(desc);
    return 0;
}

static inline void generic_handle_irq_desc(struct irq_desc *desc)
{
    desc->handle_irq(desc);
}
 

desc->handle_irq通过gic_irq_domain_map初始化的,根据不同的中断号走不同的路径

gic_irq_domain_map’如下:

static int gic_irq_domain_map(struct irq_domain *d, unsigned int irq,
                  irq_hw_number_t hw)
{
    struct irq_chip *chip = &gic_chip;

    if (static_key_true(&supports_deactivate))
        chip = &gic_eoimode1_chip;

    /* SGIs are private to the core kernel */
    if (hw < 16)
        return -EPERM;
    /* Nothing here */
    if (hw >= gic_data.irq_nr && hw < 8192)
        return -EPERM;
    /* Off limits */
    if (hw >= GIC_ID_NR)
        return -EPERM;

    /* PPIs *///PPI(Private Peripheral Interrupts)是指专用私有中断,通常用于CPU本地时钟等应用场景
    if (hw < 32) {
        irq_set_percpu_devid(irq);
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_percpu_devid_irq, NULL, NULL);
        irq_set_status_flags(irq, IRQ_NOAUTOEN);
    }
    /* SPIs */ //共享中断源允许多个处理器共享同一个中断源。SPI 中断主要用于处理外部设备引发的中断事件,如网络接口卡、硬盘控制器、串口控制器等外设的中断。
    if (hw >= 32 && hw < gic_data.irq_nr) {
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
        irq_set_probe(irq);
    }
    /* LPIs *///特定于位置的外设中断(lpi)总是基于消息的,可以来自外设,也可以来自PCle根复核。
    if (hw >= 8192 && hw < GIC_ID_NR) {
        if (!gic_dist_supports_lpis())
            return -EPERM;
        irq_domain_set_info(d, irq, hw, chip, d->host_data,
                    handle_fasteoi_irq, NULL, NULL);
    }

    return 0;
}

 

通过gic_irq_domain_map函数可以看出desc->handle_irq(desc);是handle_percpu_devid_irq和handle_fasteoi_irq二选一

handle_percpu_devid_irq如下:

void handle_percpu_devid_irq(struct irq_desc *desc)
{
    struct irq_chip *chip = irq_desc_get_chip(desc);
    struct irqaction *action = desc->action;
    unsigned int irq = irq_desc_get_irq(desc);
    irqreturn_t res;

    /*
     * PER CPU interrupts are not serialized. Do not touch
     * desc->tot_count.
     */
    __kstat_incr_irqs_this_cpu(desc);

    if (chip->irq_ack)
        chip->irq_ack(&desc->irq_data);

    if (likely(action)) {
        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, raw_cpu_ptr(action->percpu_dev_id));//此处调用的就是注册中断时传递的中断处理函数
        trace_irq_handler_exit(irq, action, res);
    } else {
        unsigned int cpu = smp_processor_id();
        bool enabled = cpumask_test_cpu(cpu, desc->percpu_enabled);

        if (enabled)
            irq_percpu_disable(desc, cpu);

        pr_err_once("Spurious%s percpu IRQ%u on CPU%u\n",
                enabled ? " and unmasked" : "", irq, cpu);
    }

    if (chip->irq_eoi)
        chip->irq_eoi(&desc->irq_data);
}

handle_fasteoi_irq如下:

void handle_fasteoi_irq(struct irq_desc *desc)
{
    struct irq_chip *chip = desc->irq_data.chip;

    raw_spin_lock(&desc->lock);

    if (!irq_may_run(desc))
        goto out;

    desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);

    /*
     * If its disabled or no action available
     * then mask it and get out of here:
     */
    if (unlikely(!desc->action || irqd_irq_disabled(&desc->irq_data))) {
        desc->istate |= IRQS_PENDING;
        mask_irq(desc);
        goto out;
    }

    kstat_incr_irqs_this_cpu(desc);
    if (desc->istate & IRQS_ONESHOT)
        mask_irq(desc);

    preflow_handler(desc);
    handle_irq_event(desc);

    cond_unmask_eoi_irq(desc, chip);

    raw_spin_unlock(&desc->lock);
    return;
out:
    if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
        chip->irq_eoi(&desc->irq_data);
    raw_spin_unlock(&desc->lock);
}

irqreturn_t handle_irq_event(struct irq_desc *desc)
{
    irqreturn_t ret;

    desc->istate &= ~IRQS_PENDING;
    irqd_set(&desc->irq_data, IRQD_IRQ_INPROGRESS);//设置中断状态
    raw_spin_unlock(&desc->lock);

    ret = handle_irq_event_percpu(desc);

    raw_spin_lock(&desc->lock);
    irqd_clear(&desc->irq_data, IRQD_IRQ_INPROGRESS);//清除中断状态
    return ret;
}
 

irqreturn_t handle_irq_event_percpu(struct irq_desc *desc)
{
    irqreturn_t retval;
    unsigned int flags = 0;

    retval = __handle_irq_event_percpu(desc, &flags);

    add_interrupt_randomness(desc->irq_data.irq, flags);

    if (!noirqdebug)
        note_interrupt(desc, retval);
    return retval;
}

irqreturn_t __handle_irq_event_percpu(struct irq_desc *desc, unsigned int *flags)
{
    irqreturn_t retval = IRQ_NONE;
    unsigned int irq = desc->irq_data.irq;
    struct irqaction *action;

    for_each_action_of_desc(desc, action) {//处理中断描述中的每一个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:
            /*
             * Catch drivers which return WAKE_THREAD but
             * did not set up a thread function
             */
            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;
}
 

handle_IPI后续再写。

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

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

相关文章

linux中最常用的用户信息命令

文章目录 linux中最常用的用户信息命令还有谁 last语法一般使用方法查看最近登陆的三个用户省略hostname显示最后一列显示主机IP地址 我是谁 whoami谁&#xff1f;who默认使用系统的运行时间显示表头信息显示登录的人员及总数 什么&#xff1f;谁&#xff1f;w (who & what…

如何理解Transformer论文中的positional encoding,和三角函数有什么关系?

大家好&#xff0c;我分享交流下这个问题。 Positional Encoding 掏出一张被无数人讲述的架构图。 Transformer 模型中的位置编码&#xff08;Positional Encoding&#xff09;是为了让模型能够考虑单词在句子中的位置。 由于 Transformer 的自注意力&#xff08;Self-Atte…

(适趣AI)Vue笔试题

&#x1f4d1;前言 本文主要是【Vue】——&#xff08;适趣AI&#xff09;Vue笔试题的文章&#xff0c;如果有什么需要改进的地方还请大佬指出⛺️ &#x1f3ac;作者简介&#xff1a;大家好&#xff0c;我是听风与他&#x1f947; ☁️博客首页&#xff1a;CSDN主页听风与他 …

Sqlmap参数设置

Sqlmap参数设置 &#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388;&#x1f388; --------------------------------------------注意---------…

设计模式Java实战,彻底学会

​这是全网最强的Java设计模式实战教程。此教程用实际项目场景&#xff0c;结合SpringBoot&#xff0c;让你真正掌握设计模式。 网址是&#xff1a;Java设计模式实战专栏介绍 - 自学精灵&#xff08;也可以百度搜索“自学精灵”&#xff09;。 本设计模式专栏的威力 用Java实…

将有序数组转换为二叉搜索树[简单]

一、题目 给你一个整数数组nums&#xff0c;其中元素已经按 升序 排列&#xff0c;请你将其转换为一棵 高度平衡 二叉搜索树。高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过1」的二叉树。 示例 1&#xff1a; 输入&#xff1a;nums [-10,-3,0,5…

nginx下日志配置和排查错误

目录 一&#xff1a;配置 二&#xff1a;排查日志 一&#xff1a;配置 在Nginx中&#xff0c;日志配置是记录服务器活动和排查问题的重要环节。以下是一些常见的Nginx日志配置选项&#xff1a; 日志级别&#xff1a;通过设置日志级别&#xff0c;可以控制日志的详细程度。常…

数据结构和算法-交换排序中的冒泡排序(过程 代码实现 算法效率 稳定性 适用链表?)

文章目录 总览冒泡排序冒泡&#xff1f;啥是冒泡排序冒泡排序过程算法实现算法性能分析稳定性冒泡排序是否适用于链表 小结 总览 冒泡排序 冒泡&#xff1f; 自然界的冒泡 啥是冒泡排序 冒泡排序过程 此时序列要求递增的 首先比较27和49&#xff0c;发现符号递增序列&…

四个模型建模及数据分析整理(基于Titanic数据集)

目录 介绍&#xff1a; 二、数据 2.1引用数据 2.2检查缺失数据 2.2.1手动检查缺失数据 2.2.2查看某一个特征值为空数据 2.3补充缺失数据 2.3.1盒图 2.3.2手动用均值填补缺失数据 2.3.3手动用类别填补缺失数据 三、数据分析 3.1男女生存比例 3.2男女生存数 3.3船舱级…

红队专题-Web安全/渗透测试-文件上传/下载/包含

文件上传/下载/包含 招募六边形战士队员利用目录穿越反弹SHELL实战测试2.2 提交报文修改检测3.2 文件内容检测绕过完整文件结构 检测 第四章&#xff1a;解析漏洞第一节 常见解析漏洞iis/nginx php fastcgi 取值错误 解析漏洞 &#xff08;配置错误&#xff09;nginx 文件名逻…

19.计数问题

题目 import java.util.Scanner;public class Main {public static void main(String[] args) {Scanner sc new Scanner(System.in);int n sc.nextInt();int res 0;int x sc.nextInt();for(int i1;i<n;i) {String s i "";for(int j0;j<s.length();j) {…

文本批量替换谁更强:Python VS. Excel公式

一、问题缘起 有人在Emeditor群里提问:有下面两张表&#xff0c;一张被替换表&#xff0c;一张参照表&#xff0c;想把替换表的内容根据对照表进行替换&#xff0c;如果对照表没有对应数据就用替换表中原有的内容。 被替换表 对照表 经过替换后的表格如下&#xff1a; 替换结…

【总线接口】1.以Xilinx开发板为例,直观的认识硬件板卡和接口

初接触硬件&#xff0c;五花八门的总线、接口一定会让你有些疑惑&#xff0c;我尝试用一系列文章来解开你的疑惑 系列文章 【总线接口】1.以Xilinx开发板为例&#xff0c;直观的认识硬件接口 【总线接口】2.学习硬件这些年接触过的硬件接口、总线 大汇总 【总线接口】…

【设计模式之美】面向对象分析方法论与实现(一):需求分析方法论

文章目录 一. 需求举例二. 对案例进行需求分析1. 第一轮基础分析2. 第二轮分析优化3. 第三轮分析优化4. 第四轮分析优化5. 最终确定需求 三. 小结 本文主要描述&#xff1a; 面向对象的需求分析方法论 一. 需求举例 假设&#xff0c;你正在参与开发一个微服务。微服务通过 HTT…

十五:爬虫-Scrapy-redis分布式

一&#xff1a;python操作redis 1.redis的安装与连接 安装 pip install redis 连接 r redis.StrictRedis(hostlocalhost,port6379,db0)2.redis数据类型相关操作 &#xff08;1&#xff09;字符串相关操作 import redis class TestString(object):# 初始化 连接redis数据库…

小游戏实战丨基于Tkinter的五子棋小游戏

文章目录 写在前面Tkinter五子棋系列文章写在后面 写在前面 本期内容&#xff1a;基于tkinter的五子棋小游戏 下载地址&#xff1a;https://download.csdn.net/download/m0_68111267/88700190 实验环境 python3.11及以上pycharmtkinter Tkinter Tkinter是Python的一个标准…

ssm基于BS的项目监管系统+jsp论文

摘 要 信息数据从传统到当代&#xff0c;是一直在变革当中&#xff0c;突如其来的互联网让传统的信息管理看到了革命性的曙光&#xff0c;因为传统信息管理从时效性&#xff0c;还是安全性&#xff0c;还是可操作性等各个方面来讲&#xff0c;遇到了互联网时代才发现能补上自古…

AP2813 双路降压恒流驱动IC 一路内置1A一路外置3A LED储能指示灯线路

产品描述 AP2813 是一款双路降压恒流驱动器,高效率、简单、内置功率管&#xff0c;适用于 5-80V 输入的高精度降 压 LED 恒流驱动芯片。内置功率管输出功率可达 12W&#xff0c;电流 1.2A。 AP2813 一路直亮&#xff0c;另外一路通过 MODE1 切换 全亮&#xff0c;爆闪。AP2813…

异常..

1.开发过程中的错误 在开发Java程序的过程中 会遇到各种各样的错误 一下是对错误的分类&#xff1a; 1.语法错误 如果产生了语法错误的话 那么就会导致编译失败 程序无法正常运行 2.逻辑错误 比如原本我想要进行加法运算 但是我将加法运算符写成了减法运算符 但是这个错误并不…

linux磁盘管理实验1

1.在安装好的linux系统中新加一块硬盘&#xff0c;将硬盘分成2个主分区&#xff0c;和2个逻辑分区&#xff0c;将其中一个逻辑分区设置成vfat&#xff08;FAT32&#xff09;分区&#xff0c;并实现开机自动挂载所有分区。 答&#xff1a;添加一个硬盘为sdb 分成2个主分区&#…