内核-中断管理

目录

1、中断

1.1、中断服务程序

2、RT-Thread 中断工作机制

2.1、中断处理过程

2.1.1、中断前导程序 

2.1.2、用户中断服务程序

2.1.3、中断后续程序

2.2、中断嵌套

2.3、中断栈

2.3.1、RT-Thread中断栈

2.4、中断的半部处理

2.4.1、底半处理

3、RT-Thread 中断管理接口

3.1、中断服务程序挂接

3.1.1、rt_hw_interrupt_install()函数

3.2、中断源管理

3.2.1、rt_hw_interrupt_mask()函数

3.2.2、rt_hw_interrupt_umask()函数

3.3、全局中断开关

3.3.1、rt_hw_interrupt_disable()函数

3.3.2、rt_hw_interrupt_enable()函数

3.4、中断通知

3.4.1、rt_interrupt_enter()函数

3.4.2、rt_interrupt_leave()函数

3.4.3、rt_interrupt_get_nest()函数


1、中断

​当CPU 正在处理内部数据时,外界发生了紧急情况,要求 CPU 暂停当前的工作转去处理这个异步事件。处理完毕后,再回到原来被中断的地址,继续原来的工作,这样的过程称为中断。实现这一功能的系统称为中断系统,申请 CPU 中断的请求源称为中断源。

注:中断是一种异常,异常是导致处理器脱离正常运行转向执行特殊代码的任何事件,如果不及时进行处理,轻则系统出错,重则会导致系统毁灭性地瘫痪。所以正确地处理异常,避免错误的发生是提高软件鲁棒性(稳定性)非常重要的一环。

1.1、中断服务程序

中断服务程序是一种需要特别注意的运行环境,它运行在非线程的执行环境下(一般为芯片的一种特殊运行模式(特权模式))。在这个运行环境中不能使用挂起当前线程的操作,因为当前线程并不存在

2、RT-Thread 中断工作机制

2.1、中断处理过程

RT-Thread 中断管理中,将中断处理程序分为中断前导程序、用户中断服务程序、中断后续程序。

2.1.1、中断前导程序 

中断前导程序主要工作如下:

1)保存 CPU 中断现场,这部分跟 CPU 架构相关,不同 CPU 架构的实现方式有差异

2)通知内核进入中断状态,调用 rt_interrupt_enter() 函数,作用是把全局变量 rt_interrupt_nest 加 1,用它来记录中断嵌套的层数。

2.1.2、用户中断服务程序

在用户中断服务程序(ISR)中,分为两种情况,第一种情况是不进行线程切换,这种情况下用户中断服务程序和中断后续程序运行完毕后退出中断模式,返回被中断的线程。另一种情况是,在中断处理过程中需要进行线程切换,这种情况会调用 rt_hw_context_switch_interrupt() 函数进行上下文切换,该函数跟 CPU 架构相关,不同 CPU 架构的实现方式有差异。

2.1.3、中断后续程序

中断后续程序主要完成的工作是:

1)通知内核离开中断状态,通过调用 rt_interrupt_leave() 函数,将全局变量 rt_interrupt_nest 减 1

2)恢复中断前的 CPU 上下文,如果在中断处理过程中未进行线程切换,那么恢复 from 线程的 CPU 上下文,如果在中断中进行了线程切换,那么恢复 to 线程的 CPU 上下文。这部分实现跟 CPU 架构相关,不同 CPU 架构的实现方式有差异

2.2、中断嵌套

在允许中断嵌套的情况下,在执行中断服务程序的过程中,如果出现高优先级的中断,当前中断服务程序的执行将被打断,以执行高优先级中断的中断服务程序,当高优先级中断的处理完成后,被打断的中断服务程序才又得到继续执行,如果需要进行线程调度,线程的上下文切换将在所有中断处理程序都运行结束时才发生,如下图所示。

2.3、中断栈

在中断处理过程中,在系统响应中断前,软件代码(或处理器)需要把当前线程的上下文保存下来(通常保存在当前线程的线程栈中),再调用中断服务程序进行中断响应、处理。

在进行中断处理时,中断处理函数中很可能会有自己的局部变量,这些都需要相应的栈空间来保存,所以中断响应依然需要一个栈空间来做为上下文,运行中断处理函数。

1)中断栈可以保存在打断线程的栈中。当从中断中退出时,返回相应的线程继续执行。

2)中断栈也可以与线程栈完全分离开来。即每次进入中断时,在保存完打断线程上下文后,切换到新的中断栈中独立运行。在中断退出时,再做相应的上下文恢复。

注:使用独立中断栈相对来说更容易实现,并且对于线程栈使用情况也比较容易了解和掌握(否则必须要为中断栈预留空间,如果系统支持中断嵌套,还需要考虑应该为嵌套中断预留多大的空间)。

2.3.1、RT-Thread中断栈

RT-Thread 采用的方式是提供独立的中断栈。即中断发生时,中断的前期处理程序会将用户的栈指针更换到系统事先留出的中断栈空间中,等中断退出时再恢复用户的栈指针。这样中断就不会占用线程的栈空间,从而提高了内存空间的利用率。且随着线程的增加,这种减少内存占用的效果也越明显。

2.4、中断的半部处理

RT-Thread 不对中断服务程序所需要的处理时间做任何假设、限制。但如同其他实时操作系统或非实时操作系统一样,用户需要保证所有的中断服务程序在尽可能短的时间内完成(中断服务程序在系统中相当于拥有最高的优先级,会抢占所有线程优先执行)。这样在发生中断嵌套,或屏蔽了相应中断源的过程中,不会耽误嵌套的其他中断处理过程,或自身中断源的下一次中断信号。

当一个中断发生时,中断服务程序需要取得相应的硬件状态或者数据。如果中断服务程序接下来要对状态或者数据进行简单处理,比如 CPU 时钟中断,中断服务程序只需对一个系统时钟变量进行加一操作,然后就结束中断服务程序。这类中断需要的运行时间往往都比较短。但对于另外一些中断,中断服务程序在取得硬件状态或数据以后,还需要进行一系列更耗时的处理过程,通常需要将该中断分割为两部分,即上半部分(Top Half)和底半部分(Bottom Half)。

注:由于将中断处理划分为Top和Bottom两个部分后,使得中断处理过程变为异步过程。这部分系统开销需要用户在使用RT-Thread时,必须认真考虑中断服务的处理时间是否大于给 Bottom Half 发送通知并处理的时间。

2.4.1、底半处理

在上半部分中,取得硬件状态和数据后,打开被屏蔽的中断,给相关线程发送一条通知(可以是 RT-Thread 所提供的信号量、事件、邮箱或消息队列等方式),然后结束中断服务程序;而接下来,相关的线程在接收到通知后,接着对状态或数据进行进一步的处理,这一过程称之为底半处理。

3、RT-Thread 中断管理接口

为了把操作系统和系统底层的异常、中断硬件隔离开来,RT-Thread 把中断和异常封装为一组抽象接口,如下图所示:

3.1、中断服务程序挂接

3.1.1、rt_hw_interrupt_install()函数

系统把用户的中断服务程序 (handler) 和指定的中断号关联起来,可调用如下的接口挂载一个新的中断服务程序。

rt_isr_handler_t rt_hw_interrupt_install(int              vector,
                                         rt_isr_handler_t handler,
                                         void            *param,
                                         const char      *name);
参数描述
vectorvector 是挂载的中断号
handler新挂载的中断服务程序
paramparam 会作为参数传递给中断服务程序
name中断的名称
返回——
return挂载这个中断服务程序之前挂载的中断服务程序的句柄

注:此API并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。

3.2、中断源管理

通常在 ISR 准备处理某个中断信号之前,我们需要先屏蔽该中断源,在 ISR 处理完状态或数据以后,及时的打开之前被屏蔽的中断源。

3.2.1、rt_hw_interrupt_mask()函数

调用 rt_hw_interrupt_mask 函数接口后,相应的中断将会被屏蔽(通常当这个中断触发时,中断状态寄存器会有相应的变化,但并不送达到处理器进行处理)。

void rt_hw_interrupt_mask(int vector);

注:中断源管理API并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。

3.2.2、rt_hw_interrupt_umask()函数

调用 rt_hw_interrupt_umask 函数接口后,如果中断(及对应外设)被配置正确时,中断触发后,将送到到处理器进行处理。 

void rt_hw_interrupt_umask(int vector);

注:中断源管理API并不会出现在每一个移植分支中,例如通常 Cortex-M0/M3/M4 的移植分支中就没有这个 API。

3.3、全局中断开关

全局中断开关也称为中断锁,是禁止多线程访问临界区最简单的一种方式,即通过关闭中断的方式,来保证当前线程不会被其他事件打断(因为整个系统已经不再响应那些可以触发线程重新调度的外部事件),也就是当前线程不会被抢占,除非这个线程主动放弃了处理器控制权。

1)使用中断锁来操作临界区的方法可以应用于任何场合,且其他几类同步方式都是依赖于中断锁而实现的,可以说中断锁是最强大的和最高效的同步方法。只是使用中断锁最主要的问题在于,在中断关闭期间系统将不再响应任何中断,也就不能响应外部的事件。所以中断锁对系统的实时性影响非常巨大,当使用不当的时候会导致系统完全无实时性可言(可能导致系统完全偏离要求的时间需求);而使用得当,则会变成一种快速、高效的同步方式。

2)函数 rt_base_t rt_hw_interrupt_disable(void) 和函数 void rt_hw_interrupt_enable(rt_base_t level) 一般需要配对使用,从而保证正确的中断状态。

注:在使用中断锁时,需要确保关闭中断的时间非常短

3.3.1、rt_hw_interrupt_disable()函数

此函数用于关闭整个系统的中断。

rt_base_t rt_hw_interrupt_disable(void);

注:由于关闭全局中断会导致整个系统不能响应中断,所以在使用关闭全局中断做为互斥访问临界区的手段时,必须需要保证关闭全局中断的时间非常短,例如运行数条机器指令的时间。

3.3.2、rt_hw_interrupt_enable()函数

此函数用于打开整个系统的中断。

void rt_hw_interrupt_enable(rt_base_t level);

注:rt_hw_interrupt_enable()这个函数用于 “使能” 中断,它恢复了调用 rt_hw_interrupt_disable()函数前的中断状态。如果调用 rt_hw_interrupt_disable()函数前是关中断状态,那么调用此函数后依然是关中断状态。恢复中断往往是和关闭中断成对使用的。

3.4、中断通知

当整个系统被中断打断,进入中断处理函数时,需要通知内核当前已经进入到中断状态。

使用 rt_interrupt_enter/leave() 的作用是,在中断服务程序中,如果调用了内核相关的函数(如释放信号量等操作),则可以通过判断当前中断状态,让内核及时调整相应的行为。例如:在中断中释放了一个信号量,唤醒了某线程,但通过判断发现当前系统处于中断上下文环境中,那么在进行线程切换时应该采取中断中线程切换的策略,而不是立即进行切换。

注:但如果中断服务程序不会调用内核相关的函数(释放信号量等操作),这个时候,也可以不调用 rt_interrupt_enter/leave() 函数。

3.4.1、rt_interrupt_enter()函数

当进入中断时,可以调用 rt_interrupt_enter() 函数,用于通知内核,当前已经进入了中断状态,并增加中断嵌套深度(执行 rt_interrupt_nest++)

void rt_interrupt_enter(void);

注:此函数仅在中断前导程序中使用。

3.4.2、rt_interrupt_leave()函数

当退出中断时,可以调用 rt_interrupt_leave() 函数,用于通知内核,当前已经离开了中断状态,并减少中断嵌套深度(执行 rt_interrupt_nest --)。

void rt_interrupt_leave(void)

注:此函数仅在中断后续程序中使用。

3.4.3、rt_interrupt_get_nest()函数

在内核需要知道当前已经进入到中断状态或当前嵌套的中断深度时,可调用 rt_interrupt_get_nest() 接口,它会返回 rt_interrupt_nest。

rt_uint8_t rt_interrupt_get_nest(void)

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值