前言
本文介绍中断机制,中断作为需要频繁使用的功能,本文将详细介绍linux内核中的中断机制。
嵌入式驱动学习专栏将详细记录博主学习驱动的详细过程,未来预计四个月将高强度更新本专栏,喜欢的可以关注本博主并订阅本专栏,一起讨论一起学习。现在关注就是老粉啦!
行文目录
1. 中断机制介绍
1.1 中断概述
当你在刷手机的时候,此时突然npy打电话来了,于是你退出刷手机状态,接听npy电话,此过程即为中断。
简单来说,中断会让CPU停止正在执行的程序,转而让CPU执行中断处理函数,执行完再返回原程序。
另外,整个操作系统就是一个中断驱动的死循环,即裸机开发中常写的while(true) {}
。其他所有的事情都是由操作系统提前注册的中断机制和其对应的中断处理函数完成。
1.2 中断的作用:
中断主要有4个用途:外设异步通知CPU,CPU间发送消息,处理CPU异常,实现系统调用
1.3 中断的产生:
中断信号的产生有以下4个来源:
1. 外设 :外设产生的中断信号是异步的,一般也叫硬件中断,硬件中断按照是否可以屏蔽分为可屏蔽中断和不可屏蔽中断,例如:网卡、磁盘、定时器都可以产生硬件中断。
2. CPU:一个CPU向另一个CPU发送中断,叫做IPI
(处理器间中断),是一种特殊的硬件中断,也是异步的。
3. CPU异常:CPU在执行指令的过程中发现异常会向自己发送中断信号,这种中断是同步的,一般也叫做软件中断。
4. 中断指令:直接用CPU指令来产生中断信号,这种中断和CPU异常一样是同步的,也可以叫做软件中断。例如,中断指令int 0x80可以用来实现系统调用。
2. 中断实现原理
2.1 中断处理流程
在单片机或裸机开发中,中断的处理方法是:
①、使能中断,初始化相应的寄存器
②、注册中断服务函数,也就是向 irqTable 数组的指定标号处写入中断服务函数
③、中断发生以后进入 IRQ 中断服务函数,在 IRQ 中断服务函数在数组 irqTable 里面查找具体的中断处理函数,找到以后执行相应的中断处理函数。
中断的执行时间不可以过长,否则会影响对新的中断信号的响应性,所以要尽量缩短中断执行场景的时间,为此对异步中断的处理方法有两种:
1、立即完全处理:
对于简单好处理的异步中断可以立即进行完全处理。
2、立即预处理(上半部)+稍后完全处理(下半部):
对于处理起来耗时的可以采取立即预处理加稍后完全处理的方式来实现中断。 立即预处理只能用直接处理来实现,而稍后完全处理的方法分两类:直接中断后处理有 softirq
(软中断)、tasklet
(微任务)、线程化中断后处理有workqueue
(工作队列)、threaded_irq
(中断线程)。
此处有一个概念:硬件中断、软件中断、硬中断、软中断是不同的概念,前两个是中断来源,后两个是中断处理方式。
2.2 中断向量表
我们每个人都有各自的身份证,代表每个人的唯一id,这样通过身份证就可以指定唯一的人。中断也是这样的,不同的中断信号有不同的处理方式,那么系统如何区分呢,即通过中断向量号。中断向量号是一个整数,CPU收到一个中断信号会根据这个信号的中断的向量号去查询中断向量表,根据中断向量表调用相应的处理函数。
中断向量表是一个表,表里面存放的是中断向量。中断服务程序的入口地址或存放中端服务程序的首地址成为中断向量,因此中断向量表是一系列中断服务程序入口地址组成的表。
3. 中断来源
3.1 软件中断
软件中断主要是两类:CPU异常和指令中断。
3.1.1 CPU异常
CPU在执行过程中遇到异常就会给自己发送异常信号,但是异常信号不一都是错误。
3.1.2 指令中断
指令中断是因为执行指令而产生了中断,指令中断是执行特定指令而发生的中断,设计这些指令的目的就是为了产生中断。其中INT n可以产生任意中断,Linux用int ix80来作为系统调用的指令。
3.2 硬件中断
硬件中断分为外设中断和处理器间中断(IPI)。
3.2.1 外设中断
外设中断和软件中断有一个很大的不同,软件中断是CPU自己给自己发送中断,而外设中断是需要外设发送中断给CPU。显然不可能将所有外设都直接连到CPU上,因此需要一个中间设备,替CPU连接到所有外设接受中断信号,这个设备叫中断控制器
不同的架构有不同的中断控制器,比如STM32这种Cortex-M内核的单片机叫NVIC,Cortex-A中叫GIC,x86上Intel开发的叫APIC。
3.2.2 处理器间中断
4. 上半部与下半部
上半部希望执行时间快,不会占用很长时间的处理;下半部多用于处理耗时的代码,保证中断函数的快进快出。
哪部分属于上半部,哪部分属于下半部没有明确规定,可以有一下一些原则:
①、要处理的内容不希望被其他中断打断,可以放入上半部
②、如果要处理的任务对时间敏感,可以放上半部
③、如果要处理的任务与硬件有关,可以放上半部
上半部的实现直接编写中断处理函数,下半部有多种实现机制,具体下文介绍。
4.1 软中断
Linux内核中使用结构体softirq_action
表示软中断。
/*
* @description : 注册软中断处理函数
* @param-nr : 要开启的软中断
* @param-action: 软中断对应的处理函数
* @return : 无
*/
void open_softirq(int nr, void (*action)(struct softirq_action *))
软中断类型枚举如下:
enum
{
HI_SOFTIRQ=0, /* 高优先级软中断 */
TIMER_SOFTIRQ, /* 定时器软中断 */
NET_TX_SOFTIRQ, /* 网络数据发送软中断 */
NET_RX_SOFTIRQ, /* 网络数据接收软中断 */
BLOCK_SOFTIRQ,
BLOCK_IOPOLL_SOFTIRQ,
TASKLET_SOFTIRQ, /* tasklet 软中断 */
SCHED_SOFTIRQ, /* 调度软中断 */
HRTIMER_SOFTIRQ, /* 高精度定时器软中断 */
RCU_SOFTIRQ, /* RCU 软中断 */
NR_SOFTIRQS
};
注册好软中断后需要通过raise_softirq()
函数触发
/*
* @description: 出发软中断
* @param-nr : 要触发的中断
* @return : 无
*/
void raise_softirq(unsigned int nr)