ARM Linux中断机制之中断处理
宏vector_stub代表的程序段如下:name, mode, correction存储传入的参数之
.macrovector_stub, name, mode, correction=0
.align5
vector_\name:
.if \correction
sublr, lr, #\correction//修正返回地址,也就是中断处理完之后要执行的指令的地址
.endif
@
@ Save r0, lr_
@ (parent CPSR)
@
///保存返回地址到堆栈,因为很快要使用r0寄存器,所以也要保存r0。sp后没有!所以sp指向的位置并没有变化。
stmiasp, {r0, lr}@ save r0, lr
mrslr, spsr
strlr, [sp, #8]@ save spsr
// 向上增长的栈。
// 此时的这个栈是中断模式下的栈,ARM下中断模式下和系统模式下的
// 栈是不同的。虽然ARM提供了七个模式,但Linux只使用了两个,一
// 个是用户模式,另一个为系统模式,所以这个栈只是一个临时性的栈。
/*
在arch/arm/include/asm/ptrace.h中有处理器的七种工作模式的定义
#define USR_MODE0x00000010
#define FIQ_MODE0x00000011
#define IRQ_MODE0x00000012
#define SVC_MODE0x00000013
#define ABT_MODE0x00000017
#define UND_MODE0x0000001b
#define SYSTEM_MODE0x0000001f
*/
mrsr0, cpsr
eorr0, r0, #(\mode ^ SVC_MODE)
msrspsr_cxsf, r0////把spsr设置为管理模式。//对spsr的所有控制为进行写操作,将r0的值全部注入spsr
@
@ the branch table must immediately follow this code
@
//andlr, lr, #0x0f// 这条指令之后lr中位spsr的低4位,上面跳转表有16项就是对应这16个状态
//movr0, sp//用r0保存堆栈指针的地址
//在对这段程序分析时要记住这段程序是以宏vector_stub的形式放在跳转表前面的。
//将跳转表中对应的地址条目存入lr。因为跳转表中每一个条目都是4个字节long,所以此处左移两位
ldrlr, [pc, lr, lsl #2]
movspc, lr@ branch to handler in SVC mode//程序跳转。
ENDPROC(vector_\name)
.endm
在此我们以在用户空间发生中断异常为例,即程序跳转到__irq_usr处。
.align5
__irq_usr:
usr_entry//usr_entry是一个宏代表一段程序插入此处,宏usr_entry所代表的程序段将在下面分析(1)
kuser_cmpxchg_check
#ifdef CONFIG_TRACE_IRQFLAGS
bltrace_hardirqs_off
#endif
//接着看get_thread_info, 它也是个宏,用来获取当前线程的地址。也将在后续分析。tsk存放的是线程结构体的地址。
/*
线程结构体原型如下在文件include/linux/sched.h中
struct thread_info {
struct task_struct*task;/* main task structure */
unsigned longflags;
struct exec_domain*exec_domain;/* execution domain */
intpreempt_count;/* 0 => preemptable, <0 => BUG */
__u32 cpu; /* should always be 0 on m68k */
struct restart_block restart_block;
};
*/
get_thread_info tsk(2)
#ifdef CONFIG_PREEMPT
//TI_PREEMPT在文件arch\arm\kernel\asm-offsets.c中定义是线程结构体thread_info 的成员preempt_count在
//结构体thread_info中的偏移
/*
内核态可剥夺内核,只有在 preempt_count 为 0 时, schedule() 才会被调用,其检查
是否需要进行进程切换,需要的话就切换。
*/
ldrr8, [tsk, #TI_PREEMPT]//获取preempt_count
addr7, r8, #1@ increment it//将该成员加一
strr7, [tsk, #TI_PREEMPT]//间改变后的值存入preempt_count
#endif
irq_handler//调用中断操作函数,irq_handler是一个宏,在后续描述(3)
#ifdef CONFIG_PREEMPT
ldrr0, [tsk, #TI_PREEMPT]
strr8, [tsk, #TI_PREEMPT]
teqr0, r7
strner0, [r0, -r0]
#endif
#ifdef CONFIG_TRACE_IRQFLAGS
bltrace_hardirqs_on
#endif
movwhy, #0//why在文件arch/arm/kernel/entry-header.S中定义为r8。:why.reqr8
bret_to_user//返回到用户态,该宏在文件 linux/arch/arm/kernel/entry-common.S中定义。(4)
UNWIND(.fnend)
ENDPROC(__irq_usr)
下面分别对上面四处宏进行分析。(usr_entry,get_thread_info tsk,irq_handler,ret_to_user)
(1)
.macrousr_entry
UNWIND(.fnstart)
UNWIND(.cantunwind)@ dont unwind the user space
//S_FRAME_SIZE在文件arch\arm\kernel\asm-offsets.c中定义表示 寄存器结构体pt_regs的大小结构体
//pt_regs中有 r0~cpsr 18个寄存器即72个字节。
subsp, sp, #S_FRAME_SIZE//为寄存器pt_regs结构体建立堆栈空间,让堆栈指针sp 指向r0 。
//stmib为存储前加,所以此处留出了用于存储r0的空间,将r1 - r12存入堆栈。sp后没加!
//所以sp指向的堆栈位置没有变,一直指向用于存储r0的存储空间。
stmibsp, {r1 - r12}
//将中断前r0,lr,spsr的值取出存放在r1 - r3中,此时的r
ARMLinux中断机制中断处 相关文章:
- Windows CE 进程、线程和内存管理(11-09)
- RedHatLinux新手入门教程(5)(11-12)
- uClinux介绍(11-09)
- openwebmailV1.60安装教学(11-12)
- Linux嵌入式系统开发平台选型探讨(11-09)
- Windows CE 进程、线程和内存管理(二)(11-09)