中断处理流程

2023-05-16

大家都说在中断处理函数中不能调度,或者说睡眠。这到底为什么?下面看中断处理的过程,从中是否能找到原因。

中断发生后会调到__irq_svc:

    .align    5
__irq_svc:
    svc_entry
    irq_handler
    ----
    svc_exit r5     @ return from exception
 UNWIND(.fnend        )
ENDPROC(__irq_svc)

看配置文件中定义了CONFIG_MULTI_IRQ_HANDLER:
    .macro    irq_handler
#ifdef CONFIG_MULTI_IRQ_HANDLER
    ldr    r1, =handle_arch_irq
    mov    r0, sp
    adr    lr, BSYM(9997f)
    ldr    pc, [r1]
#else
    arch_irq_handler_default
#endif

因使用了多个macro,看汇编会清楚些。

crash> dis -l __irq_svc

../arch/arm/kernel/entry-armv.S
0xc000df00 <__irq_svc>: sub     sp, sp, #68     ; 0x44
0xc000df04 <__irq_svc+4>:       tst     sp, #4
0xc000df08 <__irq_svc+8>:       subeq   sp, sp, #4
0xc000df0c <__irq_svc+12>:      stm     sp, {r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12}
0xc000df10 <__irq_svc+16>:      ldm     r0, {r3, r4, r5}
0xc000df14 <__irq_svc+20>:      add     r7, sp, #48     ; 0x30
0xc000df18 <__irq_svc+24>:      mvn     r6, #0
0xc000df1c <__irq_svc+28>:      add     r2, sp, #68     ; 0x44
0xc000df20 <__irq_svc+32>:      addeq   r2, r2, #4
0xc000df24 <__irq_svc+36>:      push    {r3}            ; (str r3, [sp, #-4]!)
0xc000df28 <__irq_svc+40>:      mov     r3, lr
0xc000df2c <__irq_svc+44>:      stm     r7, {r2, r3, r4, r5, r6}
0xc000df30 <__irq_svc+48>:      ldr     r1, [pc, #52]   ; 0xc000df6c
0xc000df34 <__irq_svc+52>:      mov     r0, sp
0xc000df38 <__irq_svc+56>:      add     lr, pc, #0
0xc000df3c <__irq_svc+60>:      ldr     pc, [r1]
0xc000df40 <__irq_svc+64>:      lsr     r9, sp, #13
0xc000df44 <__irq_svc+68>:      lsl     r9, r9, #13
0xc000df48 <__irq_svc+72>:      ldr     r8, [r9, #4]
0xc000df4c <__irq_svc+76>:      ldr     r0, [r9]
0xc000df50 <__irq_svc+80>:      teq     r8, #0
0xc000df54 <__irq_svc+84>:      movne   r0, #0
0xc000df58 <__irq_svc+88>:      tst     r0, #2
0xc000df5c <__irq_svc+92>:      blne    0xc000df70 <svc_preempt>
0xc000df60 <__irq_svc+96>:      msr     SPSR_fsxc, r5
0xc000df64 <__irq_svc+100>:     clrex
0xc000df68 <__irq_svc+104>:     ldm     sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc}^
0xc000df6c <__irq_svc+108>:     ldrshtgt        sp, [r3], #-236 ; 0xffffff14

看其中的ldr     pc, [r1],可以看到最终PC为c0008458
0xc000df30 <__irq_svc+48>:      ldr     r1, [pc, #52]   ; 0xc000df6c
crash> rd 0xc000df6c
c000df6c:  c073defc
r1: c073defc

0xc000df3c <__irq_svc+60>:      ldr     pc, [r1]
crash> rd c073defc
c073defc:  c0008458

找到调用的函数gic_handle_irq

crash> dis -l c0008458
/home/wenshuai/code/kernel3.4/linux_kernel/arch/arm/common/gic.c: 288
0xc0008458 <gic_handle_irq>:    mov     r12, sp

asmlinkage void __exception_irq_entry gic_handle_irq(struct pt_regs *regs)
{
    u32 irqstat, irqnr;
    struct gic_chip_data *gic = &gic_data[0];
    void __iomem *cpu_base = gic_data_cpu_base(gic);

    do {
        irqstat = readl_relaxed(cpu_base + GIC_CPU_INTACK);
        irqnr = irqstat & ~0x1c00;

        if (likely(irqnr > 15 && irqnr < 1021)) {
            irqnr = irq_find_mapping(gic->domain, irqnr);
            handle_IRQ(irqnr, regs);
            continue;
        }
        if (irqnr < 16) {
            writel_relaxed(irqstat, cpu_base + GIC_CPU_EOI);
#ifdef CONFIG_SMP
            handle_IPI(irqnr, regs);
#endif
            continue;
        }
        break;
    } while (1);
}


void handle_IRQ(unsigned int irq, struct pt_regs *regs)
{
    struct pt_regs *old_regs = set_irq_regs(regs);

    irq_enter();

    /*
     * Some hardware gives randomly wrong interrupts.  Rather
     * than crashing, do something sensible.
     */
    if (unlikely(irq >= nr_irqs)) {
        if (printk_ratelimit())
            printk(KERN_WARNING "Bad IRQ%u\n", irq);
        ack_bad_irq(irq);
    } else {
        generic_handle_irq(irq);
    }

    /* AT91 specific workaround */
    irq_finish(irq);

    irq_exit();
    set_irq_regs(old_regs);
}

从中断号到irq_desc

以中断号为索引,从全局变量数组struct irq_desc   irq_desc[NR_IRQS]中得到对应的 irq_desc,

这个全局变量是哪里赋值的?

int generic_handle_irq(unsigned int irq)

{
    struct irq_desc *desc = irq_to_desc(irq);

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


struct irq_desc *irq_to_desc(unsigned int irq)
{
    return (irq < NR_IRQS) ? irq_desc + irq : NULL;
}

struct irq_desc irq_desc[NR_IRQS] __cacheline_aligned_in_smp = {
    [0 ... NR_IRQS-1] = {
        .handle_irq    = handle_bad_irq,
        .depth        = 1,
        .lock        = __RAW_SPIN_LOCK_UNLOCKED(irq_desc->lock),
    }
};

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

crash> irq_desc | grep "handle_irq"
    handle_irq = 0xc007cbd8 <handle_bad_irq>,
 
    handle_irq = 0xc007cbd8 <handle_bad_irq>,
    handle_irq = 0xc007f608 <handle_percpu_devid_irq>,

    handle_irq = 0xc007fb20 <handle_level_irq>,

    handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
    handle_irq = 0xc007f9fc <handle_fasteoi_irq>,
 
    handle_irq = 0xc007fb20 <handle_level_irq>,
    handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
    handle_irq = 0xc007fb20 <handle_level_irq>,

    handle_irq = 0xc007fb20 <handle_level_irq>,
    handle_irq = 0xc007f884 <handle_edge_irq>,
    handle_irq = 0xc007f884 <handle_edge_irq>,
    handle_irq = 0xc007fb20 <handle_level_irq>,

    handle_irq = 0xc007fb20 <handle_level_irq>,
    handle_irq = 0xc007fb20 <handle_level_irq>,
    handle_irq = 0xc007fb20 <handle_level_irq>,
    handle_irq = 0xc0026244 <tl7689_gpio_irq_dispatch>,
    handle_irq = 0xc007f9fc <handle_fasteoi_irq>,
    
    handle_irq = 0xc007fc30 <handle_simple_irq>.

从irq_desc到irqaction

一个硬件中断上可以挂载多个action,当然要通过参数把各个action区别开,每个irq_desc 有一个指向irqaction的单向链表。


handle_level_irq(unsigned int irq, struct irq_desc *desc)
    -> handle_irq_event(desc);
handle_irq_event(struct irq_desc *desc)
{
    struct irqaction *action = desc->action;
    handle_irq_event_percpu(desc, action);
}
handle_irq_event_percpu(struct irq_desc *desc, struct irqaction *action)
    -> action->handler(irq, action->dev_id);

中断共享

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

    do {
        irqreturn_t res;

        trace_irq_handler_entry(irq, action);
        res = action->handler(irq, action->dev_id);
        trace_irq_handler_exit(irq, action, res);

        action = action->next;
    } while (action);
}

这些中断处理函数处理完后,会回到
0xc000df68 <__irq_svc+104>:     ldm     sp, {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, sp, lr, pc}^
也就是中断前的位置;

如果在中断处理过程中,发生了调度,处理完回到这里即可,这里中断使用的是当前进程的栈【这和编译选项中的8K的内核栈有关】。

原理上是可以的,只是中断处理函数的处理时间过长,影响了I/O的性能而已

中断处理相关数据结构

有两个数据结构和中断处理相关:irq_desc and irqaction.两者的关系是 irq_desc通过struct irqaction *action找到对应的 irqaction list

 struct irq_desc {
    struct irq_data irq_data;
    struct timer_rand_state *timer_rand_state;
    unsigned int *kstat_irqs;
    irq_flow_handler_t handle_irq;
    struct irqaction *action;
    unsigned int status_use_accessors;
    unsigned int core_internal_state__do_not_mess_with_it;
    unsigned int depth;
    unsigned int wake_depth;
    unsigned int irq_count;
    unsigned long last_unhandled;
    unsigned int irqs_unhandled;
    raw_spinlock_t lock;
    struct cpumask *percpu_enabled;
    const struct cpumask *affinity_hint;
    struct irq_affinity_notify *affinity_notify;
    unsigned long threads_oneshot;
    atomic_t threads_active;
    wait_queue_head_t wait_for_threads;
    struct proc_dir_entry *dir;
    struct module *owner;
    const char *name;
}

struct irqaction {
    irq_handler_t handler;
    unsigned long flags;
    void *dev_id;
    void *percpu_dev_id;
    struct irqaction *next;
    int irq;
    irq_handler_t thread_fn;
    struct task_struct *thread;
    unsigned long thread_flags;
    unsigned long thread_mask;
    const char *name;
    struct proc_dir_entry *dir;
}

irq_desc相关的操作

通过函数irq_set_chip_and_handler对irq_desc的 chip and handle赋值

arch/arm/common/gic.c
gic_irq_domain_map ->
    irq_set_chip_and_handler(irq, &gic_chip, handle_percpu_devid_irq);
    irq_set_chip_and_handler(irq, &gic_chip, handle_fasteoi_irq);

kernel/irq/chip.c
static inline void irq_set_chip_and_handler(unsigned int irq, struct irq_chip *chip,
                        irq_flow_handler_t handle)
{
    irq_set_chip_and_handler_name(irq, chip, handle, NULL);
}

void
irq_set_chip_and_handler_name(unsigned int irq, struct irq_chip *chip,
                  irq_flow_handler_t handle, const char *name)
{
    irq_set_chip(irq, chip);
    __irq_set_handler(irq, handle, 0, name);
}

int irq_set_chip(unsigned int irq, struct irq_chip *chip)
{
    unsigned long flags;
    struct irq_desc *desc = irq_get_desc_lock(irq, &flags, 0);
    if (!chip)
        chip = &no_irq_chip;
    desc->irq_data.chip = chip;
}

void
__irq_set_handler(unsigned int irq, irq_flow_handler_t handle, int is_chained,
          const char *name)
{
    unsigned long flags;
    struct irq_desc *desc = irq_get_desc_buslock(irq, &flags, 0);
    desc->handle_irq = handle;
    desc->name = name;
}

irqaction相关的操作

对应的函数是request_irq and request_threaded_irq. request_irq 是 request_threaded_irq的特例。

irqaction的创建和赋值在函数request_threaded_irq中实现,另外request_threaded_irq还创建了一个 调度属性为FIFO[RT]的内核thread,

去处理中断的后半部。

static inline int __must_checkre
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);
}

kernel/irq/manager.c
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)
{
    struct irqaction *action;
    struct irq_desc *desc;
    desc = irq_to_desc(irq);

    action = kzalloc(sizeof(struct irqaction), GFP_KERNEL);
    action->handler = handler;
    action->thread_fn = thread_fn;
    action->flags = irqflags;
    action->name = devname;
    action->dev_id = dev_id;

    __setup_irq(irq, desc, action);
}

static int
__setup_irq(unsigned int irq, struct irq_desc *desc, struct irqaction *new)
{
    struct irqaction *old, **old_ptr;

    if (new->thread_fn && !nested) {
        struct task_struct *t;

        t = kthread_create(irq_thread, new, "irq/%d-%s", irq,
                   new->name);
        /*
         * We keep the reference to the task struct even if
         * the thread dies to avoid that the interrupt code
         * references an already freed task_struct.
         */
        get_task_struct(t);
        new->thread = t;
    }
    
    ---

    if (new->thread)
       wake_up_process(new->thread);
}



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

中断处理流程 的相关文章

  • 《Java核心技术 卷1》

    目录 第4章 对象和类 lt 1 gt 静态字段和静态方法 lt 2 gt 初始化块 lt 3 gt 定义抽象类的对象变量 lt 4 gt hashCode方法得到散列码 lt 5 gt 虚拟机中的泛型类型信息 第五章 继承 第6章 接口
  • Kalibr 之 Camera-IMU 标定 (总结)

    Overview 欢迎访问 持续更新 xff1a https cgabc xyz posts db22c2e6 ethz asl kalibr is a toolbox that solves the following calibrati
  • VMware Workstations Pro 14 建立的虚拟机目录无法删除

    起因 通过VMware新建的RedHat虚拟机 xff0c 无意间的强制关机 xff0c 导致该虚拟机开机黑屏无法正常开启 xff0c 而且也关不掉 尝试删除自己创建的虚拟机目录文件 xff0c 提示文件被占用 通过任务管理器想要结束相关进
  • 矩形目标检测

    身份证 名片 书籍 考试试卷 答题卡这些检测目标都属于矩形目标检测 一 xff0c 现有技术 传统检测方法思路 xff1a 第一步 xff0c 采用滑动窗口 xff0c 设置不同的大小 xff0c 遍历图像 xff0c 得到一些目标的候选框
  • 几种常用通信协议

    通信可以形象的比喻成两个人讲话 xff1a 1 你说的别人得能听懂 xff1a 双方约定信号的协议 2 你的语速别人得能接受 xff1a 双方满足时序要求 一 IIC协议 xff1a 2C串行总线一般有两根信号线 xff0c 一根是双向的数
  • 基础篇——树莓派远程连接工具VNC不显示视频或摄像头画面解决方式

    背景故事 在树莓派上打开摄像头 xff0c 发现HDMI输出的桌面有画面 xff0c 但VNC这边没有画面 xff1b 之前有一次使用播放器播放视频也出现这个问题 xff0c 现记录解决方式 原因分析 VNC远程桌面并不是使用画面传输的方式
  • 穿越机(无人机航模)电池组装教程-电线接口

    对于动手能力强 xff0c 或者穷逼来说 xff0c 购买品牌电池玩穿越机 xff0c 或者其他航模 是非常浪费钱的 本着能自己干就不需要厂商辛苦的原则 电池组装 xff0c 我们可以自己做 非航模圈的电池 xff0c 一般都需要自带平衡电
  • UITextView

    闪退问题 scrollViewDidScroll 改为 scrollViewWillBeginDragging 禁止编辑 text setEditable NO 光标位置输入 64 param emoji 要输入的内容emoji和字符 vo
  • iView 滚动条样式

    滚动条样式 webkit scrollbar width 6px height 6px webkit scrollbar thumb background ccc webkit scrollbar track background e1e1
  • 英特尔 RealSense D415 + OpenCV 4.0 + VS2017 配置方法

    首先是Opencv 4 0 43 VS2017的配置过程 xff0c 网上已经有很多类似教程 xff0c 这里不再累赘 xff1a https www cnblogs com xinxue p 5766756 html 接下来开始配置D41
  • Android 系统调用实现函数功能--SVC指令的实现与检测

    0x0 简述 xff1a arm android中通过一些反编译的工具分析ELF文件时 xff0c 根据一些导入的系统函数可以很轻松的找到一些功能代码的实现 xff1a 查看libc中分析这些函数的实现 xff1a arm中通过SVC指令实
  • Docker学习笔记(九):Docker +Jenkins +Github持续集成

    本次配置时 xff0c jenkins需要配置在外网可访问的服务器上 xff0c 因为在git push内容到github时 xff0c 触发Webhooks并推送到jenkins服务器上 1 配置Github xff08 通过秘钥登录 x
  • opencv-python 常用函数介绍

    目录 imread xff1a 读取图片 imshow xff1a 展示图片 resize xff1a 图片等比例缩放 split xff1a 获取所有像素的颜色值 merge xff1a 根据颜色值合成图片 VideoCapture xf
  • redis 内存占用分析

    在Redis命令行中 xff0c 执行MEMORY STATS命令查询内存使用详情 Redis实例的内存开销主要由两部分组成 xff1a 业务数据的内存开销 xff0c 该部分一般作为重点分析对象 非业务数据的内存开销 xff0c 例如主备
  • php laravel 分析 redis 各个key的内存占用情况

    lt php namespace App Console Commands Tools use Illuminate Console Command use Illuminate Support Facades DB class Redis
  • centos7手动修改dns

    DNS是计算机域名系统 Domain Name System 或Domain Name Service 的缩写 xff0c 它是由域名解析器和域名服务器组成的 域名服务器是指保存有该网络中所有主机的域名和对应IP地址 xff0c 并具有将域
  • 查看并关闭占用端口

    查看占用端口 sudo lsof i 8888 关闭占用端口 sudo kill 9 2558243
  • 从水果连连看到两条序列比对

    一 序列比对 Sequence Alignment 序列比对 xff08 sequence alignment xff09 xff0c 目前是生物信息学的基本研究方法 算法类似于连连看 xff0c 规则是上下两个水果一样 xff0c 就可以
  • Nginx 配置详解

    Nginx 配置 文章目录 Nginx 配置文件结构全局配置events 配置http 配置server 配置 Rewrite一 地址重写 xff0c 地址转发 xff0c 重定向二 URL 重写语法 xff1a 使用 xff1a 三 if

随机推荐

  • 趣谈网络协议(一)

    一般来说 xff0c 网上的购物 都是基于应用层的Http协议 那么在这一层协议书我们包装了什么呢 xff0c 请看下图 一 应用层 Http头 http1 1 POST URL 正文格式 content type 长度 content l
  • JS 中 Json 数据的快速排序

    主要方法 span class token comment 升序排列 span span class token keyword function span span class token function up span span cl
  • 生物信息学导师推荐(持续更新)

    本系列会持续更新 xff0c 帮助大家找到更适合自己的导师 xff0c 注意排名不分先后 xff0c 接下来我们开始介绍 xff1a 陈润生 单位 xff1a 中国科学院生物物理研究所 方向 xff1a 长非编码RNA以及编码小肽的系统发现
  • Python 中变量的多种复制方法(常规拷贝,浅拷贝,深拷贝)

    常规拷贝 大家常用的变量复制方法 xff0c 用 61 就行 但是 xff01 但是 xff01 但是 xff01 在我们复制字典和列表时会和我们预想的不一致 接下来 xff0c 做个小实验 常规拷贝在原始变量 x 的改变后 xff0c 因
  • 图解机器学习:分类模型性能评估指标

    人间出现一种怪病 xff0c 患病人群平时正常 xff0c 但偶尔暴饮暴食 xff0c 这种病从外观和现有医学手段无法分辨 为了应对疫情 xff0c 准备派齐天大圣去下界了解情况 事先神官从人间挑选了一些健康人和患病者来对大圣的业务能力进行
  • 数据库涉及大量数据查询时的注意事项

    避免频繁连接和关闭数据库 xff0c 这样会导致IO访问次数太频繁 设计表时要建立适当的索引 xff0c 尤其要在 where 及 order by 涉及的列上建立索引 避免全表扫描 xff0c 以下情况会导致放弃索引直接进行全部扫描 避免
  • axios 使用详解

    一 安装 cnpm install axios 二 使用 三种写法 span class token comment 第一种写法 span axios span class token punctuation span span class
  • 生物序列比对的几种应用场景(图文)

    今天和大家讨论几种序列比对的应用场景 xff0c 当然只是抛转引玉 xff0c 如果小伙伴有其他应用场景 xff0c 欢迎讨论 一 物种 基因的进化 二 基因组学 2 1 比较基因组学揭示保守区 2 2 比较基因组学揭示功能元件 例如上图的
  • 图解机器学习之回归模型性能评估指标

    一个房价预测的任务 xff0c 老板说你看看这个模型咋样 xff1f 我们先绘制一个坐标轴 xff1a Y 轴为房价 xff0c X 轴为年份 将过去房价数据绘制为绿色 xff0c 回归模型绘制为蓝色 关键问题是 xff0c 怎么知道这个模
  • Chrome 将 http 域名自动跳转 https 的解决方案

    问题来源 使用 Chrome 内核浏览器 xff0c 包括 Google Chrome xff0c edge xff0c 360浏览器等 为了安全在访问同一域名时 xff0c 只要访问过带有 https 域名 xff0c 如果再使用http
  • 一文读懂相分离(图文详解)

    目录 什么是相分离 xff1f 相分离的原理 相分离的分子功能 生物信息中的相分离 一 什么是相分离 xff1f 相分离 phase separation 本身是一个物理化学概念 xff0c 二元或多元混合物会在一定的条件下分离为不同的相
  • g++: 内部错误:Killed (程序 cc1plus)

    这个原因是内存不足 xff0c 在linux下增加临时swap空间 step 1 sudo dd if 61 dev zero of 61 home swap bs 61 64M count 61 16 注释 xff1a of 61 hom
  • React 开发 | 样式模块化

    1 使用 ES6 实现样式模块化 xff0c 避免样式冲突 index module css span class token punctuation span title span class token punctuation span
  • React 开发 | 父子组件间通信

    文章目录 一 省流二 父传子例子三 子传父例子 一 省流 父组件 gt 子组件 xff1a 通过 props 传递 子组件 gt 父组件 xff1a 通过 props 传递 xff0c 但是父组件需要提取给子组件传递一个预定义的函数 二 父
  • React 开发 | 常用 Hooks

    useState 作用 用于函数式组件操作 state xff0c 类似于类组件的 setState 写法 xff1a state setState 61 useState initValue state xff1a 状态变量名setSta
  • React 项目部署后,页面404解决

    解决方法一 xff1a Nginx 配置 span class token punctuation span listen span class token number 80 span span class token punctuati
  • 一文读懂 UniProt 数据库(2023 最新版)

    一 UniProt 数据库介绍 Uniprot xff08 Universal Protein xff09 是包含蛋白质序列 xff0c 功能信息 xff0c 研究论文索引的蛋白质数据库 xff0c 整合了包括EBI xff08 Europ
  • 理解泛型调用和函数调用签名

    这里通过五个示例逐步理解泛型调用和函数调用签名 span class token comment 64 Author Zheng Lei 64 Email baimoc 64 163 com 64 Date 2023 01 18 16 29
  • 图解统计学 10 | 贝叶斯公式与全概率公式

    文章目录 概率联合概率条件概率全概率公式贝叶斯公式 过年了 xff0c 作为水果店老板的我们 xff0c 一共进了三种水果 xff0c 其中 xff1a 西瓜 xff1a 50个 香蕉 xff1a 30个 橙子 xff1a 20个 为了方便
  • 中断处理流程

    大家都说在中断处理函数中不能调度 xff0c 或者说睡眠 这到底为什么 xff1f 下面看中断处理的过程 xff0c 从中是否能找到原因 中断发生后会调到 irq svc xff1a align 5 irq svc svc entry ir