【ARMv8 异常模型入门及渐进 11 - Linux 中断上下文 irq_enterirq_exit】

news2025/4/8 14:46:40

文章目录

    • 1.1 背景
      • 1.1.1 in_interrupt 定义
      • 1.1.2 irq_count 定义
      • 1.1.3 preempt_count 各域含义
      • 1.1.4 ARMv8 中断处理流程回顾

1.1 背景

在 Linux 代码中经常会看到 WARN_ON(in_interrupt()); 或者 BUG_ON(in_interrupt()); 从名字可以看出这两句的含义是:如果当前处在 中断上下文 那么就会报 错误,并输出错误信息,那么系统时如何判断处当前运行环境是处于中断上下文的的呢?这里就需要跟踪下 in_interrupt 的函数的实现了。

1.1.1 in_interrupt 定义

in_interrupt 的定义位于preempt.h文件中
include/linux/preempt.h

/*
 * Are we doing bottom half or hardware interrupt processing?
 *
 * in_irq()       - We're in (hard) IRQ context
 * in_softirq()   - We have BH disabled, or are processing softirqs
 * in_interrupt() - We're in NMI,IRQ,SoftIRQ context or have BH disabled
 * in_serving_softirq() - We're in softirq context
 * in_nmi()       - We're in NMI context
 * in_task()      - We're in task context
 *
 * Note: due to the BH disabled confusion: in_softirq(),in_interrupt() really
 *       should not be used in new code.
 */
#define in_irq()                (hardirq_count())
#define in_softirq()            (softirq_count())
#define in_interrupt()          (irq_count())
#define in_serving_softirq()    (softirq_count() & SOFTIRQ_OFFSET)
#define in_nmi()                (preempt_count() & NMI_MASK)
#define in_task()               (!(preempt_count() & \
                                   (NMI_MASK | HARDIRQ_MASK | SOFTIRQ_OFFSET)))

注释可以看到 in_interrupt() 的含义是:判断当前进程是否处于当前处于 NMI(不可屏蔽中断) ,IRQ 中断 , 软中断 或者 软中断被屏蔽 的环境中。

线程被硬件中断打断后,如果希望执行完hardirq后就直接返回原来的线程,而不去执行pending的softirq,那么可以选择“屏蔽”softirq。

由于 in_interrupt 时 irq_count的宏定义, 接下还需要继续跟踪 irq_count 的实现。

1.1.2 irq_count 定义

include/linux/preempt.h

#define hardirq_count() (preempt_count() & HARDIRQ_MASK)
#define softirq_count() (preempt_count() & SOFTIRQ_MASK)
#define irq_count()     (preempt_count() & (HARDIRQ_MASK | SOFTIRQ_MASK \
                                 | NMI_MASK))

從上面的代码中可以看到 irq_count 的值是 preempt_count() “与” 硬中断,软中断,不可屏蔽中断的域获得到的。那么preempt_count 是如何实现的呢

preempt_count 被定义在进程 struct task_structthread_info 域中 ,也就是线程描述符中,在内核中可以通过 current_thread_info() 接口来获取进程的 thread_info:

/*
 * low level task data that entry.S needs immediate access to.
 */
struct thread_info {
        unsigned long           flags;          /* low level flags */
        mm_segment_t            addr_limit;     /* address limit */
#ifdef CONFIG_ARM64_SW_TTBR0_PAN
        u64                     ttbr0;          /* saved TTBR0_EL1 */
#endif
        int                     preempt_count;  /* 0 => preemptable, <0 => bug */
};

include/asm-generic/preempt.h

static __always_inline int preempt_count(void)
{
        return READ_ONCE(current_thread_info()->preempt_count);
}

從上面 struct thread_info 结构体定义可以看到 preempt_count 是一个 32 位变量,虽然只是一个32位变量,但由于其和中断、调度/抢占密切相关,因此该变量的作用还是十分重要的。

在 arm 中,preempt_count 是 per task 的变量

接下里继续跟踪 preempt_count 的域构成及对应域的含义。

1.1.3 preempt_count 各域含义

Linux 一共把 32bits 的 preempt_count 分成了 3个域,也就是代码中对应的:

  • HARDIRQ_MASK;
  • SOFTIRQ_MASK;
  • PREEMPT_MASK。
    在这里插入图片描述

接下来看看各个域的作用:
HARDIRQ 域:
preempt_count 中的第 1619 个 bit 表示 hardirq count,它记录了进入hardirq/top half 的嵌套次数,此时 hardirq count的值会加 1。如果 hardirq count 的值为正数,说明现在正处于hardirq上下文中。

hardirq count 占据 4 个 bits,理论上可以表示 16 层嵌套,但现在 Linux 系统并不支持hardirq的嵌套执行,所以实际使用的只有 1个bit

之所以采用4个bits,主要由于两个原因:

  • 一:因为早期 Linux 并不是将中断处理的过程分为top half 和 bottom half,而是将中断分为 fast interrupt handler 和 slow interrupt handler,而 slow interrupt handler 是可以嵌套执行的,
  • 二:某些 driver 代码可能在 top half 中重新使能 hardirq。

SOFTIRQ 域:
preempt_count 中 的第 815 个 bit 表示 softirq count,它记录了进入softirq 的嵌套次数,如果 softirq count 的值为正数,说明现在正处于 softirq上下文中。由于 softirq 在单个CPU上是不会嵌套执行的,因此和 hardirq count一样,实际只需要一个bit(bit 8)就可以了。但这里多出的 7 个 bits 并不是因为历史原因多出来的,而是另有他用

这个 "他用" 就是表示在进程上下文中,为了防止进程被 softirq 所抢占,关闭/禁止 softirq 的次数,比如每使用一次 local_bh_disable(),softirq count 高 7 个 bits(bit 9到bit 15) 的值就会加 1,使用 local_bh_enable() 则会让 softirq count 高 7 个 bits 的的值减 1。

PREEMPT 域:
在中断上下文中,调度是关闭的,不会发生进程的切换,这属于一种隐式的禁止调度,而在代码中,也可以使用 preempt_disable() 来显示地关闭调度,关闭次数由第 0 到 7 个 bits 组成的 preemption count (注意不是preempt count) 来记录。每使用一次 preempt_disable(),preemption count 的值就会加 1,使用 preempt_enable() 则会让 preemption count 的值减 1。preemption count 占 8 个 bits,因此一共可以表示最多 256 层调度关闭的嵌套。

处于中断上下文,或者显示地禁止了调度,preempt_count() 的值都不为 0,都不允许睡眠/调度的发生,这两种场景被统称为 atomic 上下文,可由 in_atomic() 宏给出判断。

1.1.4 ARMv8 中断处理流程回顾

最后我们再回顾下 ARMv8 中断处理流程,如下:

gic_handle_irq(struct pt_regs *regs)
	-->handle_domain_irq(struct irq_domain *domain, unsigned int hwirq, struct pt_regs *regs)
		--> __handle_domain_irq(struct irq_domain *domain, unsigned int hwirq
			-->irq_enter();  ------------------------------>(1)
			-->generic_handle_irq(irq);
				-->irq = irq_find_mapping(domain, hwirq);        //根据硬件中断号找到对应的软件中断号
					-->struct irq_desc *desc = irq_to_desc(irq); //根据i rq 号找到对应的struct irq_desc
					--> generic_handle_irq_desc(desc);      //进入具体中断处理
			-->irq_exit();--------------------------------->(2)

(1) irq_enter 显式告诉 Linux 内核现在要进入中断上下文了;
(2) 在处理完中断后调用 irq_exit 告诉 Linux 已经完成中断处理过程。

接下来看下 irq_enter 的实现:
linux/kernel/softirq.c

/*
 * Enter an interrupt context.
 */
void irq_enter(void)
{
        rcu_irq_enter();
        if (is_idle_task(current) && !in_interrupt()) {
                /*
                 * Prevent raise_softirq from needlessly waking up ksoftirqd
                 * here, as softirq will be serviced on return from interrupt.
                 */
                local_bh_disable();------->(1)
                tick_irq_enter();
                _local_bh_enable();------->(2)
        }
        __irq_enter();
}

(1) 关闭软中断并将 preempt_count 加1;
(2) 打开软中断并将 preempt_count 减 1。

linux/include/linux/hardirq.h

/*
 * It is safe to do non-atomic ops on ->hardirq_context,
 * because NMI handlers may not preempt and the ops are
 * always balanced, so the interrupted value of ->hardirq_context
 * will always be restored.
 */
#define __irq_enter()                                   \
        do {                                            \
                account_irq_enter_time(current);        \
                preempt_count_add(HARDIRQ_OFFSET);      \ ---->(1)
                trace_hardirq_enter();                  \
        } while (0)

(1) 在硬中断环境中将 preempt_count 加1;

推荐阅读

https://zhuanlan.zhihu.com/p/88883239
https://zhuanlan.zhihu.com/p/80680484

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

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

相关文章

运用手机多媒体之使用通知

文章目录使用通知将程序运行到手机上使用通知创建通知渠道通知的基本用法通知的进阶技巧setStyle()方法不同重要等级的通知渠道使用通知 将程序运行到手机上 在AS当中除了使用模拟器来运行我们的程序,还可以使用真机来运行我们写的程序想要将程序运行到手机上,首先需要将手机…

推荐系统学习笔记-推荐系统分布式离线训练

背景 在推荐、广告、搜索等互联网场景下&#xff0c;动则TB甚至PB级数据量。导致几乎不可能在传统单机环境下完成机器学习模型的训练。分布式机器学习训练成为称为唯一选择。 主要手段 • Spark MLlib • Parameter Server • Tensorflow Spark MLlib MLlib从功能上说与Sc…

如何利用地表温度遥感数据和气象资料计算农田地表水热通量

地表水热通量主要包括感热/显热通量和潜热通量&#xff0c;是陆-气交互以及水-热-碳循环研究的重要变量。其中&#xff0c;潜热通量是地表蒸散发的能量形式&#xff0c;对农业水资源管理、作物水分利用效率等非常关键。由于热红外遥感对地表干湿变化、以及农业干旱响应快速&…

WeakHashMap源码解析

WeakHashMap源码解析 简介 WeakHashMap 是一种 弱引用 map&#xff0c;内部的 key 会存储为弱引用&#xff0c;当 jvm gc 的时候&#xff0c;如果这些 key 没有强引用存在的话&#xff0c;会被 gc 回收掉&#xff0c;下一次当我们操作 map 的时候会把对应的 Entry 整个删除掉…

金融信息科技服务外包风险管理能力成熟度评估规范 学习笔记 附录下载地址

金融信息科技服务外包风险管理的范围 本标准规定了金融业信息科技服务外包风险管理能力成熟度评估体系以及对发包方和承包方的总体要求&#xff0c;分别对发包方、承包方的服务外包风险管理能力成熟度进行了分级定义&#xff0c;并规定了对发包方和承包方进行服务外包风险管理…

《OpenGL 模型》 渲染出帅气的暗影战士

模型Assimp流程网格模型效果Assimp 3D建模工具&#xff0c;可以让艺术家创建复杂的形状&#xff0c;Assimp库用于加载&#xff0c;如加载obj格式的文件到我们的程序之中&#xff0c;下载CMAKE用于构建该库&#xff08;会有很多问题&#xff09;&#xff0c;不过&#xff01;我…

【小程序】小程序代码的构成

目录 项目结构 1. 了解项目的基本组成结构 2. 小程序页面的组成部分 JSON配置文件 1. JSON 配置文件的作用 2. app.json 文件 3. project.config.json 文件 4. sitemap.json 文件 5. 页面的 .json 配置文件 6. 新建小程序页面 7. 修改项目首页 项目结构 1. 了解项…

别再用过时的方式了!全新版本Spring Security,这样用才够优雅!

基本使用 我们先对比下Spring Security提供的基本功能登录认证&#xff0c;来看看新版用法是不是更好。 升级版本 首先修改项目的pom.xml文件&#xff0c;把Spring Boot版本升级至2.7.0版本。 <parent><groupId>org.springframework.boot</groupId><art…

Lua 元表(Metatable)

在 Lua table 中我们可以访问对应的 key 来得到 value 值&#xff0c;但是却无法对两个 table 进行操作(比如相加)。 因此 Lua 提供了元表(Metatable)&#xff0c;允许我们改变 table 的行为&#xff0c;每个行为关联了对应的元方法。 例如&#xff0c;使用元表我们可以定义 …

STM32程序设计规范浅析

这篇博客写到“STM32基础知识篇”里&#xff0c;一方面是一个很好地对过往工作的总结&#xff0c;另一方面也是整个专栏撰写计划的开端&#xff0c;古人云&#xff1a;良好的开端是成功的一半&#xff0c;在文章的最后详细地规划了整个专栏后期的更新计划。 笔者前段时间休息的…

无人机遥感图像拼接与处理操作技术

【内容简述】&#xff1a; 无人机遥感图像采集流程&#xff1a; 无人机遥感监测介绍 无人机航线规划设计 无人机飞行软件操作 无人机航拍一般过程 无人机遥感图像拼接软件操作&#xff1a; Photoscan软件介绍 软件基本操作与实践 遥感图像拼接的一般流程 遥感图像分组拼接与点…

【centos】安装nvida CUDA平台附带安装cudnn库

目录1.安装 CUDAToolKit2.安装cudnn库1.安装 CUDAToolKit 使用 lspci | grep -i nvidia列出所有支持的GPU 安装内核开发依赖包&#xff1a; yum install kernel-devel查看内核版本号&#xff0c;用来看与开发包版本号是否一致&#xff1a; uname -r查看nvida显卡驱动&#…

设计模式之迭代器模式

Iterator design pattern 迭代器模式的概念、迭代器模式的结构、迭代器模式的优缺点、迭代器模式的使用场景、迭代器模式的实现示例、迭代器模式的源码分析 1、迭代器模式的概念 迭代器模式&#xff0c;即提供一种方法来顺序访问聚合对象内的元素&#xff0c;而不暴露聚合对象…

LeetCode HOT 100 —— 448. 找到所有数组中消失的数字

题目 给你一个含 n 个整数的数组 nums &#xff0c;其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字&#xff0c;并以数组的形式返回结果。 思路 原地哈希&#xff08;简单模拟&#xff09;&#xff1a; 核心思路&#xff1a; 因为…

Python 和 PyQt5 实现打地鼠小游戏

Python 和 PyQt5 实现打地鼠小游戏 实现效果&#xff1a; 视频效果&#xff1a; https://live.csdn.net/v/264602https://live.csdn.net/v/264602 代码&#xff1a; import random import sysfrom PyQt5.QtCore import QBasicTimer, Qt, QTimer from PyQt5.QtGui import QCo…

CSS3【基础选择器、字体样式、文本样式、行高样式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录基础选择器1. 标签选择器2. 类选择器3. id选择器字体和文本样式1.字体样式1.1 字体大小1.2 字体粗细1.3 字体样式&#xff08;是否倾斜&#xff09;1.4 常见字体系列…

JAVA中实现多线程-单例双重锁(DCL(Double Check Lock)双重锁检查)

一 .多线程 继承 Thread 类实现 Runnable 接口实现 Callable 接口线程池 重写run方法&#xff0c;创建对象&#xff0c;调用start()方法启动线程 1&#xff0c;新生状态 – 用new关键字建立一个线程后&#xff0c;该线程对象就处于新生状态。 – 处于新生状态的线程有自己的…

Netty前置知识

传统IO 这里以文件输入输出流&#xff1a;FileInputStream 、 FileOutputStream 来进行解释。由继承关系得知&#xff0c;这两个输入和输出类继承自 InputStream 和 OutputStream 这两个基础的输入、输出的抽象类&#xff0c;这时我们可以看到当我们需要读写文件的时候&#x…

leetcode--搜索

搜索1.深度优先搜索(DFS)&#xff08;1&#xff09;岛屿的最大面积(695)&#xff08;2&#xff09;省份数量&#xff08;3&#xff09;太平洋大西洋水流问题(417)2.回溯法&#xff08;1&#xff09;全排列(46)&#xff08;2&#xff09;组合(77)&#xff08;3&#xff09;单词搜…

C++ allocator设计内存管理器

文章目录allocator内存管理器基本属性类的设计关键功能的实现完整的内存管理器内存管理器的测试&#xff1a;设计自定义的String类。前情回顾&#xff1a; allocator内存管理类 allocator内存管理器 某些类需要在运行时分配可变大小的内存空间&#xff0c;一般来说我们使用容器…