05 Linux对中断的处理

代码示例
第3课.Linux异常处理体系结构

1.Linux中中断的定义

Linux中将中断划分为为硬件中断和软件中断

CPU在处理中断时,CPU是不能进行调度的。即A中断尚未处理完之前CPU是不会响应B中断的,即使B中断的优先级比A高

中断处理原则:

  • 不能嵌套(因为中断会存在上下文的保存和还原)
  • 越快越好

中断也分未上半部和下半部

  • 上半部

    处理紧急的事情,此时会关中断。无法处理其他中断

  • 下半部

    此时会开中断,处理相对而言不那么紧急的事情。会被其他中断打断。下半部的处理时间相对于上半部的处理时间会偏长一些

    中断下半部的实现有多种方法:tasklet(小任务)、work queue(工作队列)、thread irq等

    tasklet是使用软件中断来实现的

    work queue用于处理特别耗时的工作。tasklet本质上还是软中断,在中断中CPU无法进行其他调度。此时使用work queue将部分工作交由内核线程(kworker)去实现。如此便不会阻塞。

    thread irq:只需要向内核提供thread_fn,系统就会为这个函数创建一个内核线程。work queue中一个worker线程只能由一个CPU去执行。当存在多个中断的work由同一个worker线程处理时,对于多核而言就不太合适了。有可能会让有的CPU处于空闲状态。此时引入了thread irq可以为每个中断都创建一个内核线程(避免都使用同一个线程),使内核线程可以分配到个CPU上去执行。

因为开关总中断开关功能的存在,中断上半部处理完后才会处理中断下半部。即多个中断上半部对应中断下半部,中断下半部是汇总到一起来处理的
具体细节以后有时间在去深挖!

2.Linux中对中断的处理

首先对需要对linux中中断处理有一个整体框架行的认知(图片来源百问网)

GIC:通用中断控制器(硬件上只有一个此设备)

GPIO(其他模块):这里可以将GPIO和其他模块成为其对应的中断控制器。不过此控制器为软件概念上的,并无实体

整体工作流程为

上报B中断事件 上报A中断事件

外部设备触发中断事件上报 ----> GPIO中断处理 ----> GIC中断处理 ----> CPU接收中断事件

依据本人对中断的理解,这里对中断的处理简单的划分为两大部分:devicetree配置中断信息,linux注册中断

2.1 devicetree中指定中断资源

2.1.1 interrupt-controller

devicetree指定某个节点为中断控制器,必须包含interrupt-controller。表明此节点为中断控制器

2.1.2 #interrupt-cells

表示别的节点使用此中断控制器需要使用几个cells来表示哪一个中断

eg:
#interrupt-cells=<2>	表明需要两个cells来描述一个中断

一般而言第一个cells用于指定使用哪个中断,而第二个cell用于描述中断的触发类型

第二个cell的bit[3:0]用于表示中断触发类型
1 = low-to-high edge triggered		上升沿触发
2 = high-to-low	edge triggered		下降沿触发
4 = active high level-sensitive		高电平触发
8 = active low	level-sensitive		低电平触发

2.1.3 interrupt-parent

中断中中断控制器存在层级的关系(详见上图),下级中断控制器需要表明它的上级interrupt-parent是谁

2.1.4 interrupts

interrupt用于指定节点使用的具体中断,描述方法受#interrupt-cells限制

示例

i2c@7000c000 {
	gpioext: gpio-adnp@41 {
		compatible = "ad,gpio-adnp";
		interrupt-parent = <&gpio>;
		interrupts = <160 1>;

		gpio-controller;
		#gpio-cells = <1>;

		interrupt-controller;
		#interrupt-cells = <2>;
	};
......
};

2.1.5 interrupts-extended

此写法为一种比较新的写法,即此写法包含了interrupt-parent和interrupts。

eg:
interrupts-extended = <&intc1 5 1>

2.2 内核中解析设备树的资源并实现中断注册和控制

2.2.1 requset_irq注册中断

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

irq		: 中断号
handler	: 中断处理函数
flags	: 中断处理属性
name	: 设备驱动名称
dev		: 中断id

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)

requset_irq调用requset_threaded_irq注册中断处理函数时,会构造一个irqaction结构体,用于保存name、dev_id、handler、thread_fn、thread

  • handler是中断处理的上半部函数
  • thread_fn为对应的一个内核线程,当handler处理完毕后内核会唤醒其对应的内核线程,及对应的thread_fn会被调用。中断下半部
  • dev_id为中断的检索时使用,当卸载中断时可以根据此索引找到对应的action链表中的对应地址,将其卸载

2.2.2 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与hwirq之间的映射关系是通过irq_domain来实现的
相同的irq号分属于不同的irq_domain即可以两个irq都为1,也不会存在问题

2.2.3 irq_domain

我们在irq_requset中注册中断使用的是软件中断号irq,一般是基于gpio引脚号,并不意味着实际的hwirq
在实际中我们使用对应域的irq_domain去实现获取设备树的中断属性(xlate),并将hwirq转化为我们所认知的irq(map)

2.2.2 irq_chip

chip的一些操作,在我们从设备树中获取到相关的中断属性后,irq_chip中的函数会帮助我们去使能中断或者清中断等相关操作

posted @ 2023-02-14 22:26  人民广场的二道贩子  阅读(115)  评论(0编辑  收藏  举报