先来看51单片机的中断机制:
当中断到来时,CPU先把正在执行的命令的地址(命令存在单片机内部,既然在内部必然就有一个所在地址,PC指向的是当前执行的命令的下一条命令,例如:我的命令有第一条、第二条、第三条,如果CPU正在执行第二条命令,那PC是指向第三条命令)压入堆栈存着,这一步是保护现场;
接着CPU被强制跳转到中断矢量处(外部中断0的矢量为0003,外部中断1的矢量为000B,定时器0的中断矢量为0013,定时器1的中断矢量为001B,串口中断矢量为0023),通常在中断矢量中存放的是跳转指令;例如定时器0的中断矢量是000B,而我定义定时器0的中断服务函数是void
time0_interrupt() 1,那么在000B这个地址里面存放的就是代码的功能就是从当前执行程序中跳转到void time0_interrupt()
1函数去执行(至于这条跳转指令是怎么写的还不懂啊,汇编不好是硬伤啊),这是中断响应;
执行完中断服务程序后,一开始被压入堆栈的命令又弹出来继续执行,这是现场恢复;
以上是51单片机的中断过程,那么Linux也是差不多,只是Linux的中断多、代码量大、涉及的代码知识相对难,但是道理还是一样。如下:
开发板上电之后u-boot会关闭看门狗、关闭MMU、设置中断等等,设置中断就是通过这个函数:void __init early_trap_init(void)
函数部分代码是这样:
memcpy((void *)vectors, __vectors_start,
__vectors_end - __vectors_start);//这一段的意思是把中断向量里的那些跳转指令拷贝到vectors这个地址;
__vectors_start:开始时向量的所在的起始地址。
__vectors_end - __vectors_start:指的是_拷贝_vectors_start到__vectors_end的地址里的内容
注意拷贝是跳转指令不是向量,也有人把向量就认为是那些跳转指令,个人见解吧,但这个_vectors_start地址里存放的真的是跳转指令(估计看到这会有点模糊,为什么不是仅仅拷贝跳转地址而是向量地址里面的内容呢?看后面就知道了)
memcpy((void *)vectors + 0x200, __stubs_start,
__stubs_end - __stubs_start);//这一段的意思是,把中断服务程序拷贝到)vectors +
0x200,当然这应该只是一个起始地址,中断服务代码量大的话,占用的内存也会相应大。至于其他两个参数和上面的函数是基本一致的。
等中断来了之后,先是压栈等等工作,保存现场,接着跳转指令就会跳转到
asmlinkage void
__exception_irq_entry asm_do_IRQ(unsigned
int irq, struct pt_regs *regs)
函数去执行,
irq:终断号
regs:用于保存中断之前执行着的地址(关于前面的不解,现在该明白了吧,它是先把跳转指令等拷贝进来,执行跳转的时候顺便保存现场,哎,非要跟51不一样)
简单介绍这个函数的一些代码:
struct pt_regs *old_regs =
set_irq_regs(regs);这个函数就是记录中断现场;
irq_enter();进入中断处理过程,但是里面的功能好复杂,愣是没懂
generic_handle_irq(irq);这个函数需要细说,irq是中断号,函数原型是
static inline void
generic_handle_irq(unsigned int irq)
{
generic_handle_irq_desc(irq, irq_to_desc(irq));
}
它调用了这个函数
static inline void
generic_handle_irq_desc(unsigned int irq, struct irq_desc
*desc)
{
desc->handle_irq(irq, desc);
}
看函数的参数:
irq:中断标号
irq_to_desc(irq)这其实是一个带参宏,是这样的:
#define irq_to_desc(irq)
(&irq_desc[irq])
irq_desc是一个结构体,个人理解是这样的
:每个中断或者说每一组中断对应一个中断标号,每一个中断标号又会对应一个这样的结构体,类似于一个字符设备会对应一个cdev结构体,struct irq_desc
irq_desc[irq]就代表某个中断的结构体,其中断号是irq,通过这个结构体变量(这个结构体变量是数组,说白了,假如irq=1,那irq_desc[1]就纯碎是一个结构体变量)去访问结构体成员,结构体成员很多,例如中断名称、对应中断号的服务函数、中断状态等。
函数内容desc->handle_irq(irq, desc);
关于handle_irq就好多话说了,在irq_desc结构体里面有这样一个成员,是这么写的:
irq_flow_handler_t handle_irq;
(其实就好比int a;就是定义了一个整形的变量a,而irq_flow_handler_t
handle_irq;,就是定义了一个irq_flow_handler_t型的变量handle_irq)
这个irq_flow_handler_t是一个宏,如下:
typedef void (*irq_flow_handler_t)(unsigned
int irq, struct irq_desc *desc);
这又怎么解释呢?
先看这个:char a[10],这个就是定义数组a,这个数组有十个元素,且元素都是字符型的;
再看typedef char T[10];
T
a;这两句一起句的功能也是同上,至于为什么俺也不知道怎么说,但是就是这样,它就是这样,记住就好;说白了就是T代替了char [10];
再看int
(*p)(int,int)
意思是定义了一个指针p,这个指针指向的是一类(切记是一类而不是一个)函数,这类函数有两个int型的参数,函数返回值是int型;
再看typedef int
(*PT)(int,int);
PT p; 呐,这两句加起来的功能就和上面一样,记住就好,用多了就会理解
通过这个例子,应该都知道为什么会有handle_irq(irq,
desc);了吧,开始明明就是个变量而已,摇身一变就函数啦!!!
关于
asmlinkage void
__exception_irq_entry asm_do_IRQ(unsigned
int irq, struct pt_regs *regs)
以上就是中断执行部分。