5.2中断系统中的设备树——Linux对中断处理的框架及代码流程简述

news2024/11/26 13:57:03

当发生中断时,CPU会跳到一个固定的地址去执行代码,这个固定的地址就被称为中断向量

ARM920T为例,它的中断向量默认是地址24(0x18)的地方。那么,就可以在这里放一条跳转指令

一系列的跳转指令用来处理各种异常,中断也是一种异常

这些指令就被称为vector异常向量表

对于ARM9来说,vector可以放在0地址,也可以放在0xffff 0000地址(使能MMU,开启虚拟地址之后)。

对于其他芯片,vector的存放地址可能不一样。但是不管怎么样,对于大部分的芯片来说,它的软件中都应该保存一个vector,用来处理各种异常。

在linux内核中,也有一个vector。

打开arch\arm\kernel\entry-armv.S,可以找到一个vectors,里面存放的就是异常向量。

当发生中断时,应该执行这条指令,跳到 vector_irq 标号所示的段去执行代码。

W(b)    vector_irq

搜索代码,是找不到 vector_irq 标号的,它是通过vector_stub宏来设置的。

宏的设置如下。

结合宏的定义,可以看出就是设置一个标号为vector_irq的段,省略号省略的是宏中描述的其他代码,这些代码暂不研究,它们应该会调用后面的相关函数

比如,当中断发生在用户态时,调用“.long    __irq_usr”;当中断发生在管理模式下时,调用“.long    __irq_svc”。

不管是用户态还是管理模式,最终,它们都会调用同一个函数

看一下它们是如何调用到同一个函数的。

首先是__irq_usr,在同一个文件中可以找到__irq_usr的标号。

根据中断的一般处理流程:

  1. 保存现场;
  2. 处理中断;
  3. 恢复现场;

可以猜测,usr_entry是保存现场,irq_handler是处理中断,ret_to_user_from_irq是恢复现场。

再来看一下__irq_svc

类似的,svc_entry应该是保存现场,irq_handler是处理中断,svc_exit应该是恢复现场和退出中断。

那么,也就是说__irq_usr__irq_svc,都是通过irq_handler来处理中断。

查看一下irq_handler的定义,它的定义也是在同个文件下。

可以看到,irq_handler是一个handle_arch_irq是一个函数指针,最终会调用到handle_arch_irq所指向的函数

猜测在这个函数中,应该会读取中断控制器判断是哪个中断发生了然后调用对应的中断处理函数处理中断

既然handle_arch_irq是一个函数指针,那么是在哪里设置的呢?

搜索handle_arch_irq,可以在 kernel\irq\handle.c 文件中发现对应的设置函数set_handle_irq。

再搜索一下set_handle_irq,看看是哪里调用这个函数。

从搜索结果可以知道,对于每款芯片,它们各自都会调用set_handle_irq函数,来设置它们自己的中断处理的入口函数。

对应s3c2440芯片,则使用s3c24xx_handle_irq函数,作为自己的中断处理的入口函数。

s3c24xx_handle_irq函数如下。

可以从这个函数开始分析,内核对中断的处理框架

在分析代码之前,先总结一下。

假设这是CPU中断控制器

可以看到,这个中断控制器有32位,每一位代表一种中断,也就是说这个中断控制器可以向CPU发出32种中断,并且每一种中断的处理函数都不一样。

问:在Linux内核里面,怎么管理这么多种中断和它们的处理函数呢?

答:最简单的方法就是,创建一个指针数组,0号元素对应0号中断,1号元素对应1号中断。在数组中存放对应中断的处理函数。

事实上,内核上的处理方法也是类似的,只不过数组项用一个结构体irq_desc(中断描述结构体)来表示。

struct irq_desc {
	struct irq_common_data	irq_common_data;
	struct irq_data		irq_data;
	unsigned int __percpu	*kstat_irqs;
	irq_flow_handler_t	handle_irq;
#ifdef CONFIG_IRQ_PREFLOW_FASTEOI
	irq_preflow_handler_t	preflow_handler;
#endif
	struct irqaction	*action;	/* IRQ action list */
	unsigned int		status_use_accessors;
	unsigned int		core_internal_state__do_not_mess_with_it;
	unsigned int		depth;		/* nested irq disables */
	unsigned int		wake_depth;	/* nested wake enables */
	unsigned int		irq_count;	/* For detecting broken IRQs */
	unsigned long		last_unhandled;	/* Aging timer for unhandled count */
	unsigned int		irqs_unhandled;
	atomic_t		threads_handled;
	int			threads_handled_last;
	raw_spinlock_t		lock;
	struct cpumask		*percpu_enabled;
	const struct cpumask	*percpu_affinity;
#ifdef CONFIG_SMP
	const struct cpumask	*affinity_hint;
	struct irq_affinity_notify *affinity_notify;
#ifdef CONFIG_GENERIC_PENDING_IRQ
	cpumask_var_t		pending_mask;
#endif
#endif
	unsigned long		threads_oneshot;
	atomic_t		threads_active;
	wait_queue_head_t       wait_for_threads;
#ifdef CONFIG_PM_SLEEP
	unsigned int		nr_actions;
	unsigned int		no_suspend_depth;
	unsigned int		cond_suspend_depth;
	unsigned int		force_resume_depth;
#endif
#ifdef CONFIG_PROC_FS
	struct proc_dir_entry	*dir;
#endif
#ifdef CONFIG_GENERIC_IRQ_DEBUGFS
	struct dentry		*debugfs_file;
	const char		*dev_name;
#endif
#ifdef CONFIG_SPARSE_IRQ
	struct rcu_head		rcu;
	struct kobject		kobj;
#endif
	struct mutex		request_mutex;
	int			parent_irq;
	struct module		*owner;
	const char		*name;
} ____cacheline_internodealigned_in_smp;

在内核中,有一个irq_desc数组,里面放的是irq_desc结构体。

 主要关注irq_desc结构体的三个成员:

  1. handle_irq,总的中断处理函数;
  2. action,是一个链表,指向一个或多个irqaction结构体。
    irqaction结构体里面,有一个handler成员,它是一个函数指针,指向我们提供的具体的中断处理函数
  3. irq_data;

问:在irq_desc结构体中有一个函数指针handle_irq,在irqaction结构体中又有一个函数指针handler,它们都是处理中断的,为什么会有两个处理中断的函数呢?

答:显然,他们的作用不会完全相同。

首先看一下handle_irq做什么事情:

  1. 调用action链表中的handler
  2. 清中断;

这样做有一个好处,就是我们只需要提供具体的中断处理函数,专注于我们自己要做的事情就可以了,而不需要我们自己去清中断

那么,handle_irq要怎么清中断呢?

清中断是芯片相关的操作,这就要用到irq_data了。

struct irq_data {
	u32			mask;
	unsigned int		irq;
	unsigned long		hwirq;
	struct irq_common_data	*common;
	struct irq_chip		*chip;
	struct irq_domain	*domain;
#ifdef	CONFIG_IRQ_DOMAIN_HIERARCHY
	struct irq_data		*parent_data;
#endif
	void			*chip_data;
};

irq_data结构体中含有一个chip成员,它是一个指向irq_chip结构体的指针。

irq_chip结构体里面含有各种函数指针,每一个函数指针指向的函数都代表一个功能,比如清中断(irq_ack),关闭中断(irq_disable),使能中断(irq_enable)等等。

irq_chip结构体在include\linux\irq.h中定义,内有各个函数指针的详细说明。)

struct irq_chip {
	struct device	*parent_device;
	const char	*name;
	unsigned int	(*irq_startup)(struct irq_data *data);
	void		(*irq_shutdown)(struct irq_data *data);
	void		(*irq_enable)(struct irq_data *data);
	void		(*irq_disable)(struct irq_data *data);

	void		(*irq_ack)(struct irq_data *data);
	void		(*irq_mask)(struct irq_data *data);
	void		(*irq_mask_ack)(struct irq_data *data);
	void		(*irq_unmask)(struct irq_data *data);
	void		(*irq_eoi)(struct irq_data *data);

	int		(*irq_set_affinity)(struct irq_data *data, const struct cpumask *dest, bool force);
	int		(*irq_retrigger)(struct irq_data *data);
	int		(*irq_set_type)(struct irq_data *data, unsigned int flow_type);
	int		(*irq_set_wake)(struct irq_data *data, unsigned int on);

	void		(*irq_bus_lock)(struct irq_data *data);
	void		(*irq_bus_sync_unlock)(struct irq_data *data);

	void		(*irq_cpu_online)(struct irq_data *data);
	void		(*irq_cpu_offline)(struct irq_data *data);

	void		(*irq_suspend)(struct irq_data *data);
	void		(*irq_resume)(struct irq_data *data);
	void		(*irq_pm_shutdown)(struct irq_data *data);

	void		(*irq_calc_mask)(struct irq_data *data);

	void		(*irq_print_chip)(struct irq_data *data, struct seq_file *p);
	int		(*irq_request_resources)(struct irq_data *data);
	void		(*irq_release_resources)(struct irq_data *data);

	void		(*irq_compose_msi_msg)(struct irq_data *data, struct msi_msg *msg);
	void		(*irq_write_msi_msg)(struct irq_data *data, struct msi_msg *msg);

	int		(*irq_get_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool *state);
	int		(*irq_set_irqchip_state)(struct irq_data *data, enum irqchip_irq_state which, bool state);

	int		(*irq_set_vcpu_affinity)(struct irq_data *data, void *vcpu_info);

	void		(*ipi_send_single)(struct irq_data *data, unsigned int cpu);
	void		(*ipi_send_mask)(struct irq_data *data, const struct cpumask *dest);

	unsigned long	flags;
};

稍微总结一下内核中如何处理中断:

当发生中断时,CPU跳到中断向量处执行。

首先,保存现场;之后,会跳转到第一个处理中断的入口函数(对于s3c2440是s3c24xx_handle_irq函数)。

在这个函数里面,CPU应该去读取中断寄存器,分辨发生的是哪个中断,获得硬件中断号——hwirq,然后通过hwirq可以在irq_desc数组中找到与之对应的那一项,这一项的下标叫做虚拟中断号——virq,hwirq和virq是不相同的,它们之间存在一定的偏移值

找到对应的数组项后,会调用这一数组项中的handle_irq,由handle_irq来调用对应的action成员中的handler函数,然后调用对应数组项中的irq_data.chip.irq_ack来清中断。

共享中断

假设0号中断外接了网卡和摄像头,它们是一个的关系,也就是不论是网卡还是摄像头发生了中断,最终都会触发0号中断。

 那么,irq_desc数组中对应项的action链表应该如下,第一项是irq_net,处理网卡的中断,下一项通过next相连,是irq_cam,用来处理摄像头的中断。

当检测到0号中断发生,irq_netirq_camhandler函数都会被调用,在irq_nethandler函数中判断是否是发生了网卡中断,不是的话立刻返回;同理,irq_camhandler函数也是类似的操作。

这种action链表的形式支持共享中断,对于共享中断,里面的每一个处理函数都会被执行一次,因为我们没有办法在handle_irq里面判断是哪个硬件产生了中断,这个判断过程必须交给硬件的中断处理函数来实现

以s3c2440的4号中断为例,4号中断外接了4个引脚EINT4,5,6,7,这4个引脚产生了中断,都会触发4号中断。

对于这种情况,当然也可以像irq_netirq_cam那样处理,在4号中断对应的数组项中添加4个irq_action

但是,有更好的方法。因为这种情况下,有办法分辨发生的是哪个中断(以2440为例,可以读取EINTPEND寄存器,判断发生了哪个中断),此时不需要将每个中断处理函数都调用一次。

此时就可以将handle_irq指向s3c_irq_demux函数,这是一个分发函数

  1. 在分发函数中,首先会读取寄存器,进一步分析触发了哪个中断,得到对应的hwirq';
  2. 根据hwirq'得到virq;
  3. 调用irq_desc[virq].handle_irq;

硬件中断号和虚拟中断号

插讲一下硬件中断号和虚拟中断号。

有CPU,中断控制器INTC,还有下一级的中断控制器subINTC。

读INTC的到hwirq,读subINTC,得到hwirq'。

将硬件中断号,加上一个偏移值,就可以得到对应的虚拟中断号(irq_desc数组对应项的下标)。

中断处理流程:
假设中断结构如下:
sub int controller ---> int controller ---> cpu

发生中断时,cpu跳到"vector_irq", 保存现场, 调用C函数handle_arch_irq

handle_arch_irq:
a. 读 int controller, 得到hwirq
b. 根据hwirq得到virq
c. 调用 irq_desc[virq].handle_irq

如果该中断没有子中断, irq_desc[virq].handle_irq的操作:
a. 取出irq_desc[virq].action链表中的每一个handler, 执行它
b. 使用irq_desc[virq].irq_data.chip的函数清中断

如果该中断是由子中断产生, irq_desc[virq].handle_irq的操作:
a. 读 sub int controller, 得到hwirq'
b. 根据hwirq'得到virq
c. 调用 irq_desc[virq].handle_irq

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

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

相关文章

基于配置系统和流水线的热更新方案

文章目录背景方案调研具体方案方案优缺点背景 最近我们要在一个新的 App 上增加热更新的能力,按照以往的设计思路,需要后台一起参与,并提供对应的接口,具体的接口如下: 接口参数返回值备注uploadBasePkgappVersion&a…

接口管理工具YApi怎么用?颜值高、易管理、超好用

众多接口管理工具如雨后春笋搬冒出。让人欣慰的是,有许多优秀作品来自国内,包含YApi和rap。看着中文的官网,熟悉的汉语,不禁让人暗爽。当然这也就带来另一个弊端,因为使用基数少,所以参考资料少。我们想学习…

Linux时间的获取与使用

Linux系统时间有两种。 (1)日历时间。该值是自协调世界时(UTC)1970年1月1日00:00:00这个特定时间以来所经过的秒数累计值。基本数据类型用time_t保存。最后通过转换才能得到我们平时所看到的24小时制或者12小时间制的时间。 (2)…

使用WSL获得Ubuntu系统环境

文章目录使用WSL获得Ubuntu系统环境为什么要用WSL什么是WSLWSL部署安装Windows Terminal软件使用WSL获得Ubuntu系统环境 为什么要用WSL WSL作为Windows10系统带来的全新特性,正在逐步颠覆开发人员既有的选择。 传统方式获取Linux操作系统环境,是安装完…

凯撒加密Caesar cipher

凯撒加密的由来凯撒加密正是凯撒大帝发明的,是一种古典的加密凯撒率军征服高卢,袭击日耳曼和不列颠,古罗马开启了走出意大利,征服全欧洲的征程仅用8年时间征服高卢后,凯撒率军越过卢比孔河,驱赶政敌&#x…

振弦采集模块参数配置工具的使用

振弦采集模块参数配置工具的使用 通常情况下,在计算机端对模块进行测试、读写时,可使用一些通用的免费工具完成,如基于 MODBUS 通讯协议的调试工具 MODSCAN、通用串口调试助手等, 这些工具可以通过网络搜索下载使用,在…

03【Response、ServletContext】

文章目录03【Response、ServletContext】一、HTTP响应概述1.1 什么是HTTP响应:1.2 响应信息的组成:1.2.1 响应行1.2.2 响应头1.2.3 响应体1.3 Http协议小结二、HttpServletResponse对象2.1 设置响应行2.2.1 设置响应状态码2.2.2 常见响应码1)…

入选IDC报告,美创科技数据安全管理平台实力领跑

近日,国际权威研究咨询机构IDC发布《中国数据安全基础设施管理平台市场洞察,2022》报告。本次报告对行业用户以及技术提供商深入访谈,挑选出具有代表性的数据安全基础设施管理平台产品和方案,美创数据安全管理平台入选&#xff0c…

软件测试---概念篇

本文主要介绍软件测试相关的一些基础概念.主要内容包括 : 什么是需求 什么是bug 什么是测试用例 开发模型和测试模型 配置管理和软件测试 一 : 什么是需求 满足用户期望或正式规定文档(合同、标准、规范)所具有的条件和权能,包含用户需求和软…

【Kubernetes 企业项目实战】02、基于 Prometheus 和 K8s 构建智能化监控告警系统(中)

目录 一、安装和配置 node-exporter 1.1 node-exporter介绍? 1.2 安装 node-exporter 二、Prometheus server 安装和配置 2.1 创建 sa 账号,对 sa 做 rbac 授权 2.2 创建 prometheus 数据存储目录 2.3 安装 Prometheus server 服务 (…

Python再入手-03

又过了半年,已经完全忘了当时8月份的python工作了。这半年,先是跑合同,然后张罗出海,完了又搞了两次验收,还做了两次汇报,开了无数的会,忙坏了。 现在,得重新张罗电磁这档子事儿了。下面记录下最近的操作。 1 打开编程环境。 都忘了怎么打开环境了,翻一下以前的博客…

Ant Design使用

目录官网在项目中使用下载引入方法1:全部引入方法2:按需引入-手动加载方法3:按需引入-自动加载组件Anchor作用:用于跳转到页面指定位置案例1-基础使用案例2-添加偏移量案例3-指定容器总结官网 Ant Design官网 在项目中使用 在react中使用Ant Design 下载 使用如…

【无人机学习之Mission Planner】RTK/GPS Inject 学习

█ 【无人机学习之Mission Planner】RTK/GPS Inject 学习 █ 系列文章目录 提示:这里是收集了无人机的相关文章 【无人机学习】无人机基础知识【无人机学习】Mission Planner(pc端)和QGroundControl(android端)【无人…

P1102 A-B 数对

题目背景 出题是一件痛苦的事情! 相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 AB Problem,改用 A-B 了哈哈! 题目描述 给出一串正整数数列以及一个正整数 CC,要求计算出所有满足 A - B CA−BC 的数对的…

vb.net多功能白板(集成:绘图,编辑,批注,橡皮,图片处理,拍摄,裁剪,旋转等功能

根据上一次的自定义白板,我已经更新了很多内容了 这一次打算再细一点 初始化程序:所有的整体变量(作者提醒,请不要直接照抄代码,可以和作者发的文件进行学习和参考 Public ListOfPen As New List(Of Bitmap)Public L…

ArcGIS如何进行自动矢量化操作

这里我们在网络上找一幅高中地理课本上看的等高线图给大家能进行操作演示。 等高线图 01 地理配准 1、定义投影 给数据框定义一个投影,右键Layers>Properties>Coordinate System>Projected Coordinate Systems>Gauss Kruger>Beijing1954> Be…

雅思经验总结(1)

听力技巧:听sections 3就是看你何时进入状态,还有审题,之后就是听but,其他的转折词什么yet because however什么都非常的少,最主要的还是but,注意bus之后的话,其余的什么细节题就是说还要听懂文…

Biome-BGC生态系统模型区域模拟

Biome-BGC是利用站点描述数据、气象数据和植被生理生态参数,模拟日尺度碳、水和氮通量的有效模型,其研究的空间尺度可以从点尺度扩展到陆地生态系统。在Biome-BGC模型中,对于碳的生物量积累,采用光合酶促反应机理模型计算出每天的…

Java面向对象进阶之static

目录static静态关键字static:修饰成员变量,内存机制static是什么、修饰成员变量的方法总结static修饰成员变量的内存原理static:修饰成员方法、内存机制static修饰成员方法的基本用法总结static修饰成员方法的内存原理static的注意事项static…

计算机组成原理习题二

计算机组成原理习题二 文章目录计算机组成原理习题二1、某指令系统的指令格式如下:答案:(1)152301Q1101 010011 000 001I10,I21,Z/C0,D/I0,故为变址寄存器2寻址,EA(I2)A063215301063516Q。 (4)…