中断和异常处理程序的嵌套执行

https://blog.csdn.net/denglin12315/article/details/121703669

一、历史

早前的Linux内核版本,中断分为两种:

1. 快中断,申请的时候带IRQF_DISABLED标记,在IRQ HANDLER里面不允许新的中断进来;

2. 慢中断,申请的时候不带IRQF_DISABLED标记,在IRQ HANDLER里面允许新的其他中断嵌套进来。

老的Linux内核中,如果一个中断服务程序不想被别的中断打断,我们能看到这样的代码:

request_irq(FLOPPY_IRQ, floppy_interrupt,- IRQF_DISABLED, "floppy", NULL)

二、现在

在2010年如下的commit中,IRQF_DISABLED被作废了:

https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=e58aa3d2d0cc

它的commit log清晰地解释中断嵌套可能引入的一些risk,比如stack溢出等。也就是说,从这个commit开始,实际Linux已经不再支持中断的嵌套, 也没有快慢中断的概念了,IRQF_DISABLED标记也作废了。在IRQ HANDLER里面,无论一个中断设置还是不设置IRQF_DISABLED, 内核都不会开启CPU对中断的响应:

这个作废的IRQF_DISABLED标记,在内核已经没有任何的意义了。后来,这个标记本身,在内核里面也被删除了,彻底成为过往:

三、硬件

中断发生后,一般硬件会自动屏蔽CPU对中断的响应,而软件层面上,直到IRQ HANDLER做完,才会重新开启中断。比如,对于ARM处理器而言,exception进来的时候,硬件都会自动屏蔽中断:

也就是说,当ARM处理器收到中断的时候,它进入中断模式,同时ARM处理器的CPSR寄存器的IRQ位会被硬件设置为屏蔽IRQ。

Linux内核会在如下2个时候重新开启CPSR对IRQ的响应:

1. 从IRQ HANDLER返回中断底半部的SOFTIRQ

2. 从IRQ HANDLER返回一个线程上下文

从1大家可以看出,SOFTIRQ里面是可以响应中断的。

异常处理

Linux arm64 系统调用过程学习记录

中断处理

流程与异常处理类似。

el1_irq
->el1_interrupt_handler handle_arch_irq //handle_arch_irq = gic_handle_irq->irq_handler  \handler->gic_handle_irq->handle_domain_irq->irq_enter;->->preempt_count_add(HARDIRQ_OFFSET);->generic_handle_irq_desc;-> desc->handle_irq = handle_edge_irq //handle_irq = handle_edge_irq or handle_level_irq ...->handle_irq_event->handle_irq_event_percpu->__handle_irq_event_percpu->res = action->handler(irq, action->dev_id);->irq_exit;->arm64_preempt_schedule_irq //内核抢占

IRQ数据结构

arm64 对应的hw_interrupt_type没找到。

为中断处理程序保存寄存器的值

通过kernel_entry和kernel_exit保存恢复寄存器
kernel_entry和kernel_exit

对于ARM处理器而言,exception进来的时候,硬件都会自动屏蔽中断:

也就是说,当ARM处理器收到中断的时候,它进入中断模式,同时ARM处理器的CPSR寄存器的IRQ位会被硬件设置为屏蔽IRQ。

这里重点强调一下ERET,以arm64为例,调用该指令后,PSTATE恢复SPSR_ELn的值,PC恢复ELR_ELn的值.
https://blog.csdn.net/weixin_42135087/article/details/107227624

ARMv8-A Process State, PSTATE介绍
https://blog.csdn.net/longwang155069/article/details/105204547

do_IRQ

__do_IRQ

这两节对应下面流程

-> desc->handle_irq = handle_edge_irq //handle_irq = handle_edge_irq or handle_level_irq ...->handle_irq_event->handle_irq_event_percpu->__handle_irq_event_percpu->res = action->handler(irq, action->dev_id);
void handle_edge_irq(struct irq_desc *desc)
{raw_spin_lock(&desc->lock);desc->istate &= ~(IRQS_REPLAY | IRQS_WAITING);if (!irq_may_run(desc)) {desc->istate |= IRQS_PENDING;mask_ack_irq(desc);goto out_unlock;}/** If its disabled or no action available then mask it and get* out of here.*/if (irqd_irq_disabled(&desc->irq_data) || !desc->action) {desc->istate |= IRQS_PENDING;mask_ack_irq(desc);goto out_unlock;}kstat_incr_irqs_this_cpu(desc);/* Start handling the irq */desc->irq_data.chip->irq_ack(&desc->irq_data);do {if (unlikely(!desc->action)) {mask_irq(desc);goto out_unlock;}/** When another irq arrived while we were handling* one, we could have masked the irq.* Renable it, if it was not disabled in meantime.*/if (unlikely(desc->istate & IRQS_PENDING)) {if (!irqd_irq_disabled(&desc->irq_data) &&irqd_irq_masked(&desc->irq_data))unmask_irq(desc);}handle_irq_event(desc);} while ((desc->istate & IRQS_PENDING) &&!irqd_irq_disabled(&desc->irq_data));out_unlock: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;record_irq_time(desc);for_each_action_of_desc(desc, action) {irqreturn_t res;/** If this IRQ would be threaded under force_irqthreads, mark it so.*/if (irq_settings_can_thread(desc) &&!(action->flags & (IRQF_NO_THREAD | IRQF_PERCPU | IRQF_ONESHOT)))lockdep_hardirq_threaded();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 %pS 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);fallthrough;    /* to add to randomness */case IRQ_HANDLED:*flags |= action->flags;break;default:break;}retval |= res;}return retval;
}

挽救丢失的中断

kernel/irq/chip.c

irq_startup
->irq_enable
->check_irq_resendint check_irq_resend(struct irq_desc *desc, bool inject)
{int err = 0;/** We do not resend level type interrupts. Level type interrupts* are resent by hardware when they are still active. Clear the* pending bit so suspend/resume does not get confused.*/if (irq_settings_is_level(desc)) {desc->istate &= ~IRQS_PENDING;return -EINVAL;}if (desc->istate & IRQS_REPLAY)return -EBUSY;if (!(desc->istate & IRQS_PENDING) && !inject)return 0;desc->istate &= ~IRQS_PENDING;if (!try_retrigger(desc))err = irq_sw_resend(desc);/* If the retrigger was successfull, mark it with the REPLAY bit */if (!err)desc->istate |= IRQS_REPLAY;return err;
}

软中断及tasklet

ksoftirqd

这一节的代码对应下面的smpboot_thread_fn

static struct smp_hotplug_thread softirq_threads = {.store          = &ksoftirqd,.thread_should_run    = ksoftirqd_should_run,.thread_fn      = run_ksoftirqd,.thread_comm       = "ksoftirqd/%u",
};static __init int spawn_ksoftirqd(void)
{cpuhp_setup_state_nocalls(CPUHP_SOFTIRQ_DEAD, "softirq:dead", NULL,takeover_tasklets);BUG_ON(smpboot_register_percpu_thread(&softirq_threads));return 0;
}static int smpboot_thread_fn(void *data)
{struct smpboot_thread_data *td = data;struct smp_hotplug_thread *ht = td->ht;while (1) {set_current_state(TASK_INTERRUPTIBLE);preempt_disable();...if (!ht->thread_should_run(td->cpu)) {preempt_enable_no_resched();schedule();} else {__set_current_state(TASK_RUNNING);preempt_enable();ht->thread_fn(td->cpu);}}static void run_ksoftirqd(unsigned int cpu)
{local_irq_disable();if (local_softirq_pending()) {/** We can safely run softirq on inline stack, as we are not deep* in the task stack here.*/__do_softirq();local_irq_enable();cond_resched();return;}local_irq_enable();
}

深入理解LINUX内核 笔记 第四章 中断和异常相关推荐

  1. Linux内核探讨-- 第四章

    本文是个人分析<Linux内核设计与实现>而写的总结,欢迎转载,请注明出处: http://blog.csdn.net/dlutbrucezhang/article/details/122 ...

  2. TMS320C6748开发视频教程笔记 第9章 中断与异常

    第9章 中断与异常 9 中断与异常 9-1 中断 9-1-1 中断类型 9-1-2 中断结构 9-1-3 中断事件选择 9-1-4 中断错误事件 9-1-5 中断向量表 9-1-6 中断嵌套 9-2 ...

  3. 深入理解LINUX内核 第三版2.6 笔记

    目录 第二章 内存寻址 分段 P41 Linux分段 P46 硬件中的分页 P50 第三章 进程 第四章 中断和异常 第五章 内核同步 第六章 定时测量 第七章 进程调度 第八章 内存管理 页框管理 ...

  4. 深入理解Linux内核-第3版 译者序、前言、目录 内核2.6.11

    一.译者序 Linux是一个全新的世界,世界意味着博大精深,而新或许代表对旧的割舍和扬弃,加在一起,就是要我们在割舍和扬弃的同时还要积累知识到博大精深的地步,这容易做到吗?是的,这不容易做到.Gera ...

  5. 《Linux内核设计与实现》 第八周读书笔记 第四章 进程调度

    20135307 张嘉琪 第八周读书笔记 第四章 进程调度 调度程序负责决定将哪个进程投入运行,何时运行以及运行多长时间,进程调度程序可看做在可运行态进程之间分配有限的处理器时间资源的内核子系统.只有 ...

  6. 深入理解LINUX内核(影印版第3版)》的笔记

    书名: 深入理解LINUX内核(影印版第3版) 作者: Daniel P.Bovet/Marco Cesati 副标题: Understanding the Linux Kernel 页数: 923 ...

  7. 笔记:深入理解Linux内核(二)

    笔记:深入理解Linux内核(二) 二零二一年十月二十四日 文章目录 笔记:深入理解Linux内核(二) 第二章:内存寻址 内存地址 硬件中的分段 段选择符和段选择器 段描述符 快速访问段描述符 分段 ...

  8. 深入理解 C 指针阅读笔记 -- 第四章

    Chapter4.h #ifndef __CHAPTER_4_ #define __CHAPTER_4_/*<深入理解C指针>学习笔记 -- 第四章*//*指针数组 -- 意思就是这是一个 ...

  9. 《深入理解Linux内核》 读书笔记

    深入理解Linux内核 读书笔记 一.概论 操作系统基本概念 多用户系统 允许多个用户登录系统,不同用户之间的有私有的空间 用户和组 每个用于属于一个组,组的权限和其他人的权限,和拥有者的权限不一样. ...

最新文章

  1. 西卡 你要浮出水面啦
  2. python量化投资必背代码-基于python的开源量化交易,量化投资架构
  3. 常用 Git 命令清单(转)
  4. javascript高级程序设计 学习笔记 第五章 上
  5. 代码审查:程序员内炼之道
  6. Mplayer后台播放没有声音
  7. mac11.14 mysql_mysql 5.7 11 章 数据类型(1)
  8. Android SharedPreferences最佳实践
  9. mysql undo log回收_MySQL5.7 可以回收(收缩)undo log回滚日志物理文件空间
  10. 使用IAR在线调试功能显示数据变化曲线
  11. JAVA中console方法怎么用,java的Console类的使用方法及实例
  12. Unity插件学习记录 -- SW Actions
  13. 怎样做一个拥有全局观的人
  14. 网页默认打印:fixed布局的内容会在每页打印,遮挡其他内容
  15. VS+QT没有自动生成.ui对应的ui_XXXX.h头文件问题
  16. 白杨SEO:从5118站长工具备案内参发现百度SEO批量建站优化玩法
  17. 软考高级考完了,怎么评职称?
  18. Hadoop 3.0 纠删码技术分析(Erasure Coding)
  19. 显控触摸屏SA-4.3A下载程序提示:选择的HMI型号不匹配
  20. “PPT中如何插入和提取swf文件”的解决方案

热门文章

  1. Elsevier latex模板 三级标题斜体改直体
  2. Web前端复习-12、jQuery进阶
  3. 赛码网常见接受参数的方法
  4. Web3.0到底是什么?它关我们普通人什么事?
  5. Centos7安装socks5代理服务器
  6. 智汀家庭云-开发指南android端:存储池分区
  7. ConvNeXt网络结构搭建
  8. Android 新版v2签名渠道包工具
  9. Quartz的一些基本知识
  10. springboot使用yml文件如何多环境(dev、test、prod)配置