RT-thread内核之异常与中断

1、什么是中断?异步

中断有两种,一种是CPU自己在执行程序的过程当中产生的,一种是由CPU外部产生的。 cpu外部中断,就是一般所讲的“中断”(interrupt)。对于执行程序来讲,这种“中断”的发生彻底是异步的,由于不知道何时会发生。CPU对其的响应也彻底是被动的, 能够经过“关中断”指令关闭对其的响应。 然而由软件产生的中断通常是由专设的指令,如X86中的“INT n”在程序中有意产生的, 是主动的,同步的。只要CPU执行一条INT指令,在开始执行下一条指令以前必定会进入中 断服务程序。这种主动的中断称为“陷阱”(trap)async

从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器的输入引脚上,而后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断本身当前正在处理的工做,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就能够对这个中断进行适当的处理。不一样的设备对应的中断不一样,而每一个中断都经过一个惟一的数字标识,这些值一般被称为中断请求线(IRQ)。
中断可分为同步(synchronous)中断和异步(asynchronous)中断:函数

1. 同步中断是当指令执行时由 CPU 控制单元产生,之因此称为同步,是由于只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间,好比系统调用。ui

2. 异步中断是指由其余硬件设备依照 CPU 时钟信号随机产生,即意味着中断可以在指令之间发生,例如键盘中断。spa

2、什么是异常?操作系统

同步中断又称为异常(exception);异步中断则被称为中断(interrupt)。咱们一般讲的中断指的都是异步中断,即cpu外部中断。
1.中断可分为可屏蔽中断(Maskable interrupt)和非屏蔽中断(Nomaskable interrupt)。
2.异常可分为故障(fault)、陷阱(trap)、终止(abort)三类。线程

这些类别之间的异同点以下:指针

类别             缘由                   异步/同步                   返回行为
中断      来自I/O设备的信号          异步                 老是返回到下一条指令
陷阱      有意的异常                    同步                 老是返回到下一条指令
故障      潜在可恢复的错误           同步                 返回到当前指令
终止      不可恢复的错误              同步                 不会返回code

3、中断处理过程blog

当中断产生时,处理器将按以下的顺序执行:
• 保存当前处理机状态信息
• 载入异常或中断处理函数到PC寄存器
• 把控制权转交给处理函数并开始执行
• 当处理函数执行完成时,恢复处理器状态信息
• 从异常或中断中返回到前一个程序执行点

中断使得CPU能够在事件发生时才予以处理,而没必要让CPU接二连三地查询是否有相应的事件发生。经过两条特殊指令:关中断和开中断可让处理器不响应或响应中断(在关闭中断期间,一般处理器会把新产生的中断挂起,当中断打开时马上进行响应)。在执行中断服务例程的过程当中,若是有更高优先级别的中断源触发中断,因为当前处于中断处理上下文环境中,根据不一样的处理器构架可能有不一样的处理方式:好比新的中断等待挂起直到当前中断处理离开后再行响应,但这在硬实时环境中不容许发生;或者新的高优先级中断打断当前中断处理过程,而去直接响应这个更高优先级的新中断源,这称为中断嵌套,而中断是否可以嵌套,通常由MCU处理器的中断机制决定,如stm32中只有新中断的抢占优先级比当前中断高时,才能打断当前中断进入新的中断服务函数。

在系统响应中断前,软件代码(或处理器)须要把当前线程的上下文保存下来(一般保存在当前线程的线程栈中),再调用中断服务例程进行中断响应、处理。在进行中断处理时(实质是调用用户的中断服务例程函数),中断处理函数中极可能会有本身的局部变量,这些都须要相应的栈空间来保存,因此中断响应依然须要一个栈空间来作为上下文运行中断处理函数。中断栈能够保存在打断的线程栈中,当从中断中退出时,返回相应的线程继续执行。中断栈也能够与打断线程栈彻底分离开来,即每次进入中断时,在保存完被打断的线程上下文后,切换到新的中断栈中独立运行;在中断退出时,再作相应的上下文恢复。

使用独立中断栈相对来讲更容易实现,而且对于线程栈使用状况也比较容易了解掌握(不然必需要为中断栈预留空间,若是系统支持中断嵌套,还须要考虑应该为嵌套中断预留多大的空间)。RT-Thread采用的方式是提供独立的中断栈,即中断发生时,中断的前期处理程序会将用户的栈指针更换到系统事先留出的中断栈空间中,等中断退出时再恢复用户的栈指针。这样中断就不会占用线程的栈空间,从而提升了内存空间的利用率,且随着任务的增长,这种减小内存占用的的效果也越明显。以stm32的cotex-M3/M4为例,cotex-M3/M4中拥有两个堆栈指针,然而它们是banked,所以任一时刻只能使用其中的一个:

      主堆栈指针(MSP):复位后缺省使用的堆栈指针,用于操做系统内核,以及异常与中断处理。

      进程堆栈指针(PSP):由用户的应用程序代码(如线程切换)使用。堆栈指针的最低两位永远是0,这意味着堆栈老是4 字节对齐的。

由此能够看出,在一个实际运行系统里有两大部分:一是操做系统和中断,一是用户应用程序。它们使用的资源是不同的,从中断(线程调度时产生的中断)返回到用户应用程序线程时,系统使用的堆栈指针也从MSP变成PSP。

4、与操做系统相关的中断接口:在src/irq.c中

void rt_interrupt_enter(void);
void rt_interrupt_leave(void); 
当整个系统被中断打断,进入中断处理函数时,OS须要知道当前已经进入到中断状态。
rt_interrupt_enter函数用于通知OS,当前已经进入了中断状态;rt_interrupt_leave函数用于通知OS,已经离开中断状态。一般来讲,OS须要知道这样的运行状态,这样在中断服务例程中,若是调用了OS相关的调用,OS好及时调整相应的行为,例如进行任务切换时应该采起中断中任务切换的策略,而不是当即进行切换。可是若是中断服务例程很显然、很必然地不会去调用OS相关的函数,此时也能够不调用rt_interrupt_enter/leave函数。

rt_uint8_t rt_interrupt_get_nest(void);
获取当前中断嵌套计数值,大于0说明处于中断服务中,大于1说明存在中断嵌套

5、与MCU相关的中断接口:以stm32f4为例

关闭中断:在libcpu/arm/cotex-m4/context_rvds.S中用汇编语言实现
rt_base_t rt_hw_interrupt_disable(void);
函数返回中断前的系统中断状态。
当系统关闭了中断时,就意味着当前线程/代码不会被其余事件所打断(由于整个系统已经再也不对外部事件响应),也就是当前线程不会被抢占(由于线程切换时用到了PendSV_Handler),除非这个线程主动让出处理器。

打开中断:在libcpu/arm/cotex-m4/context_rvds.S中用汇编语言实现
void rt_hw_interrupt_enable(rt_base_t level);
调用这个函数接口将恢复调用rt_hw_interrupt_disable前的中断状态,level是上一次关闭中断时返回的值。打开中断每每是和关闭中断成对使用的,用于恢复关闭中断前的状态。
注意:调用这个接口并不表明着确定打开中断,而是恢复关闭中断前的状态,若是调用rt_hw_interrupt_disable()前是关中断状态,那么调用此函数后依然是关中断状态。
相关文章
相关标签/搜索