概述
1.1.1.1退回用户态
从中断返回到用户态是由ret_to_user_from_irq进行处理的,在恢复寄存器现场前,需要处理抢占、检查信号等等。
/**
*从中断返回用户态,在软中断或者中断处理函数退出时,系统确保已经关闭了中断。
*/
ENTRY(ret_to_user_from_irq)
/**
*从任务的TI_FLAGS标志判断是否需要处理抢占或者信号。
*/
ldrr1, [tsk, #TI_FLAGS]
tstr1, #_TIF_WORK_MASK
/**
*处理抢占或者信号
*/
bnework_pending
/**
*运行到这里,说明没有抢占或者信号需要处理,或者已经处理完毕,开始退回用户态了。
*/
no_work_pending:
/**
*退回用户态时,必然会打开中断,因此这里记录下打开中断的事实,供调试用。
*/
#if defined(CONFIG_IRQSOFF_TRACER)
asm_trace_hardirqs_on
#endif
/* perform architecture specific actions before user return */
/**
*在返回用户态前,处理各个体系结构的钩子,对我们分析的单板来说,没有钩子需要处理。
*/
arch_ret_to_user r1, lr
/**
*恢复寄存器现场,并切回用户态。
*/
restore_user_regs fast = 0, offset = 0
ENDPROC(ret_to_user_from_irq)
在切换回用户态前,需要处理抢占和信号:
work_pending:
/**
*检查任务的_TIF_NEED_RESCHED,如果置位,则说明需要处理任务抢占,在这里调度到高优先级任务。
*/
tstr1, #_TIF_NEED_RESCHED
bnework_resched
/**
*接着处理信号。
*/
tstr1, #_TIF_SIGPENDING|_TIF_NOTIFY_RESUME
/**
*没有信号需要处理,则跳转到no_work_pending并退回用户态。
*/
beqno_work_pending
movr0, sp@ 'regs'
movr2, why@ 'syscall'
tstr1, #_TIF_SIGPENDING@ delivering a signal?
movnewhy, #0@ prevent further restarts
/**
*这里处理信号
*/
bldo_notify_resume
/**
*然后重新关中断并判断是否有更多任务需要处理。
*/
bret_slow_syscall@ Check work again
/**
*这里处理抢占,注意这里可以调用schedule,而不用调用preempt_schedule。这是有原因的。
*另外,这个标号也不能随意移到其他地方。因为调用schedule后,流程会转到ret_slow_syscall。
* ret_slow_syscall上会关中断,然后将中断、异常返回流程重新处理一次。
*需要关中断的原因,是schedule函数会强制将中断打开。
*/
work_resched:
blschedule
/*
* "slow" syscall return path."why" tells us if this was a real syscall.
*/
ENTRY(ret_to_user)
ret_slow_syscall:
disable_irq@ disable interrupts
1.1.1.2从svc32模式进入中断
当中断嵌套或者中断打断系统调用等异常时,中断会从svc32模式进入中断。在开始中断处理前,系统仍然需要保存寄存器现场。这是通过调用宏svc_entry来实现的。
/**
*当从svc模式进入中断处理程序时,使用本宏保存寄存器现场到堆栈中,并形成pt_regs结构传递给C函数。
*/
.macrosvc_entry, stack_hole=0
UNWIND(.fnstart)
UNWIND(.save {r0 - pc})
/**
*将当前指针向低地址移动,以保存寄存器现场。这里减去4是为了将sp指向pt_regs中r1的位置。
*/
subsp, sp, #(S_FRAME_SIZE + stack_hole - 4)
#ifdef CONFIG_THUMB2_KERNEL/*新内核支持将内核编译为THUMB-2,以节省代码空间,我们的系统不支持这个功能*/
SPFIX(strr0, [sp])@ temporarily saved
SPFIX(movr0, sp)
SPFIX(tstr0, #4)@ test original stack alignment
SPFIX(ldrr0, [sp])@ restored
#else
SPFIX(tstsp, #4)
#endif
SPFIX(subeqsp, sp, #4)
/**
*将r1-r12保存到堆栈中。
*/
stmiasp, {r1 - r12}
/**
* r0,sp,lr,spsr已经被汇编代码使用,因此需要根据r0从中断栈(我们目前正在使用的是svc栈)中取出
*/
ldmiar0, {r1 - r3}
/**
* r5指向pt_regs的ARM_sp即r13
*/
addr5, sp, #S_SP - 4@ here for interlock avoidance
movr4, #-1@""""""""
/**
*将r0调整到刚进入宏的位置
*/
addr0, sp, #(S_FRAME_SIZE + stack_hole - 4)
SPFIX(addeqr0, r0, #4)
/**
*保存r0,同时将sp向下调整4字节,现在sp指向pt_regs了。
*/
strr1, [sp, #-4]!@ save the "real" r0 copied
@ from the exception stack
movr1, lr
@
@ We are now ready to fill in the remaining blanks on the stack:
@
@r0 - sp_svc
@r1 - lr_svc
@r2 - lr_, already fixed up for correct return/restart
@r3 - spsr_
@r4 - orig_r0 (see pt_regs definition in ptrace.h)
@
stmiar5, {r0 - r4}/*将中断栈中的数据保存到pt_regs */
.endm
当不是从用户态进入中断时,中断处理代码要稍显复杂一点,主要是需要处理抢占:
/**
*从svc32模式进入中断的处理过程
*/
__irq_svc:
/**
*首先保存寄存器现场。
*/
svc_entry
/**
*进入中断后,系统自动将中断关闭,这里调用trace_hardirqs_off记录下中断被关闭的事实,用于跟踪调试。
*/
#ifdef CONFIG_TRACE_IRQFLAGS
bltrace_hardirqs_off
#endif
#ifdef CONFIG_PREEMPT
/**
*对可抢占内核来说,这里将任务的抢占计数加1,在整个中断处理过程中,进程都不能被抢占。
*/
get_thread_info tsk
ldrr8, [tsk, #TI_PREEMPT]@ get preempt count
addr7, r8, #1@ increment it
strr7, [tsk, #TI_PREEMPT]
#endif
irq_handler
#ifdef CONFIG_PREEMPT
/**
*恢复抢占计数。
*/
strr8, [tsk, #TI_PREEMPT]@ restore preempt count
/**
*将任务的TI_FLAGS标志加载到r0中,这样后面会根据r0判断_TIF_NEED_RESCHED,以处理任务抢占
*/
ldrr0, [tsk, #TI_FLAGS]@ get flags
/**
*如果在进入中断前,系统处于系统调用状态,那么抢占计数就可能为0.
*这里比较抢占计数是否为0,如果为0,则进行抢占处理。
*/
teqr8, #0@ if preempt count != 0
/**
*如果系统关抢占了,那么强制针r0清0,这样就不可能调用svc_preempt
*/
movner0, #0@ force flags to 0
/**
*如果系统没有关抢占,并且任务存在_TIF_NEED_RESCHED标志,则调用svc_preempt处理抢占。
*/
tstr0, #_TIF_NEED_RESCHED
blnesvc_preempt
#endif
/*在此中断处于关闭状态,从pt_regs中获得中断前的SPSR寄存器,接下来将会用这个寄存器恢复状态。*/
ldrr4, [sp, #S_PSR]@ irqs are already disabled
/*如果恢复状态后,将会打开中断,则调用trace_hardirqs_on进行跟踪*/
#ifdef CONFIG_TRACE_IRQFLAGS
tstr4, #PSR_I_BIT
bleqtrace_hardirqs_on
#endif
/**
*退回svc32模式
*/
svc_exit r4@ return from exception
UNWIND(.fnend)
ENDPROC(__irq_svc)
处理抢占的代码并不复杂,如下:
#ifdef CONFIG_PREEMPT
svc_preempt:
movr8, lr
/**
*这里调用preempt_schedule_irq处理抢占调度,今后在分析调度时,将会详细介绍这个函数。
*/
1:blpreempt_schedule_irq@ irq en/disable is done inside
/**
* preempt_schedule_irq返回时,会重新将中断关闭,这里加载TI_FLAGS标志是安全的。
*/
ldrr0, [tsk, #TI_FLAGS]@ get new tasks TI_FLAGS
tstr0, #_TIF_NEED_RESCHED
/**
*如果任务没有抢占标志,那么退回上层继续处理,恢复寄存器现场,返回上层中断。
*/
moveqpc, r8@ go again
/**
*否则表示任务再次被抢占,循环处理抢占。
*/
b1b
#endif
1.1.1.3退回svc32模式
从中断退出的代码如下:
#ifndef CONFIG_THUMB2_KERNEL
.macrosvc_exit, rpsr
msrspsr_cxsf, rpsr /*恢复rpsr */
#if defined(CONFIG_CPU_V6)
/**
*恢复r0寄存器
*/
ldrr0, [sp]
/**
*由于发生了中断,需要执行strex指令,这样上层中断中的spinlock会认为排它性装载失效,重启spinlock循环。
*在mips等体系结构中,这是由硬件完成的。可能ARM硬件不能完成这件事。
*/
strexr1, r2, [sp]@ clear the exclusive monitor
/**
*恢复所有寄存器,并恢复cpsr。将处理器状态切回中断前。
*/
ldmibsp, {r1 - pc}^@ load r1 - pc, cpsr
#elif defined(CONFIG_CPU_32v6K)
clrex@ clear the exclusive monitor
ldmiasp, {r0 - pc}^@ load r0 - pc, cpsr
#else
ldmiasp, {r0 - pc}^@ load r0 - pc, cpsr
#endif
.endm
最后
以上就是无心丝袜为你收集整理的linux过滤错误rzhi,《LINUX3.0内核源代码分析》第二章:中断和异常的全部内容,希望文章能够帮你解决linux过滤错误rzhi,《LINUX3.0内核源代码分析》第二章:中断和异常所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复