精华内容
下载资源
问答
  • 还请各位朋友能够给我指出来,我将不胜感激,谢谢~前言笔者在《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务程序又是如何被执行的呢?...

    笔者能力有限,如果文中出现错误的地方,还请各位朋友能够给我指出来,我将不胜感激,谢谢~

    前言

    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地阐述这个概念。

    中断的概念

    当 CPU 正在处理某件事情的时候,外部发生的某一事件请求 CPU 迅速去处理,于是,CPU 暂时中止当前的工作,转去处理所发生的事件。中断服务处理完该事件以后,再回到原来被中止的地方,继续原来的工作,这样的过程称之为中断,示意图如下:

    4dea0c58de2b713e974dca7cc7e342a8.png
    中断执行示意图

    中断响应及处理过程

    回顾函数调用的过程,子程序由主程序进行调用,从而完成执行。但是中断服务子程序并没有被主程序进行调用,中断服务子程序的执行是通过中断请求完成的,也就是说中断服务子程序可以发生在主程序执行的随意位置,那现在就面临一个问题了,如果当CPU 正在执行函数调用的子程序的内容的时候产生了一个中断请求,那么这个时候 CPU 将暂停执行函数调用的子程序的内容,转而去执行中断服务子程序的内容,如果不进行额外的处理,那么函数调用的子程序的相关数据将丢失,因此在执行中断服务子程序之前,CPU 必须要保存发生中断的那个地方的相关信息,这个操作用专业的术语来讲就是保护现场,保护现场之后,CPU 将执行中断服务子程序的内容,执行完中断服务子程序的内容之后,CPU 要回到刚刚暂停的地方继续执行,另外在返回之前,CPU 还要进行恢复现场,恢复现场之后,就可以返回到暂停的地方继续执行了,下面是整个过程的示意图:

    a3ab32048d591f3a7374debaa9ca4a8b.png
    中断响应示意图

    通过上述示意图我们也可以看到在返回地址这个地方,中断服务子程序和函数调用子程序的返回地址所遵循的原理是一样的,函数调用子程序的返回地址是函数调用指令的下一条指令的地址,而在上述示意图中的 N 和 N+1 的含义也是类似的,当 CPU 执行到第 N 条指令的时候,CPU 接收到了一个中断请求,在执行完第 N 条指令之后,转而去执行中断服务子程序的内容,然后中断服务子程序的返回地址对应的是第 N+1 条指令的地址。

    中断的堆栈占用

    在刚刚所述的内容中,说到 CPU 在执行中断服务子程序的内容之前,需要保护现场,那保护现场这个操作具体是怎么实现的呢?这个时候,就要用到我们的堆栈了。在这里拿 ARM Cortex M3 举例,在响应中断时所做的第一个操作就是保护现场,它会依次把 xPSR,PC,LR,R12以及 R3-R0 由硬件自动压入适当的堆栈中,注意,这里是自动压入堆栈,也就是说如果我们看对应的汇编代码是看不到这部分压栈操作的。另外,我们知道对于 ARM Cortex M3 的堆栈指针来说,它存在两个,一个是主堆栈指针(MSP),一个是线程堆栈指针(PSP),其中主堆栈指针是复位后默认使用的堆栈指针,用于操作系统内核和中断处理程序,线程堆栈指针(PSP)是由用户的应用程序代码所使用。那么在执行现场保护时将相关寄存器的值压入堆栈,应该使用哪个堆栈指针呢?这也是存在一个原则的,如果在响应中断时,当前的代码正在使用线程堆栈(PSP),那么将使用线程堆栈指针(PSP)进行压栈,否则将使用主堆栈指针(MSP)。另外在 CPU 进入中断服务子程序之后,所涉及的堆栈操作所使用的堆栈一直是主堆栈指针(MSP)。为了更直观的展示这个过程,下图是发生中断请求后,堆栈的变化示意图:

    5f354b7e95395104ffa3ea8c2f0d32c6.png
    中断堆栈调用示意图

    通过上图我们可以很清楚地看到在响应中断时产生的保护现场操作,堆栈明显增长了,而在执行完中断服务子程序的内容之后,又将执行恢复现场的操作,这个时候堆栈的内容又减少了。为了更清楚地展示压入堆栈寄存器的操作,笔者在这里也给出上述图中堆栈粉色部分的详细内容,图片如下:

    9af600b20f51a5523710bf34a093a4a3.png
    保护现场堆栈内容

    上述就是保护现场时所压入堆栈的相关寄存器,另外还需注意的一点是当所涉及的中断服务子程序逻辑比较复杂的时候,就需要更多的寄存器了,这个时候就需要用到 R4-R11 了,但是这部分寄存器是不能进行自动压栈的,也就是说如果在中断服务子程序中使用到这部分寄存器的时候就需要进行手动压栈,那么这部分的压栈操作在汇编层面就能看到了。

    中断向量表

    在上述所阐述的内容中,我们知道了中断会在主程序的任意发生中断请求,从而执行中断服务子程序的内容,也阐述了在执行中断服务子程序的内容之前需要进行保护现场的操作,以及执行完中断服务子程序的内容之后需要进行恢复现场。现在我们再来思考,在 CPU 中,中断源不止一种,可以是按键按下所触发的一个外部中断,也可能是在使用串行通信时,收到数据所触发的一个中断,亦或是在 CPU 中定义的一个定时中断由于设置的时间到了而触发的定时中断,这个时候,就浮现一个问题了,要如何将这一个一个的中断源与其各自的中断服务子程序所一一对应起来呢?换句更为通俗的话来讲就是当 CPU 接收到一个中断信号时,CPU 将如何找到对应的中断服务子程序进行执行呢?这个时候,就需要中断向量表了,下面是中断向量表的特点:

    • 中断向量表在 CPU 中是一段连续的存储空间

    • 中断向量表在 CPU 复位后有默认的起始地址

    • 每一个中断在中断向量表中都有对应的表项,该表项的值为该中断源对应的中断服务程序的地址

    • 由程序代码确定中断向量表中的每个表项

    上述特点说中断向量表都存在默认的起始地址,在这里依旧拿 ARM Cortex M3 内核来看,它的中断向量表默认的起始地址是从地址 0x0000 0000 开始的,如下图所示:

    82e49b7de49489a2bb19dc017073378f.png
    中断向量表

    当然这只是一部分,并不是全部的表项。有了中断向量表之后,那么当 CPU 接收到中断请求的时候,就会根据这个中断请求的信号去查这个表,从而查找到其所对应的中断服务子程序的地址,然后将这个地址赋值给 PC 指针寄存器就,那么 CPU 就可以完成中断服务子程序的执行了,对于 PC 指针寄存器不是太清楚地朋友可以看笔者的这篇文章 《程序是如何在 CPU 中运行的(二)》

    中断服务函数的写法

    中断服务函数的写法不同的 CPU 有各自不同的写法,对于 ARM Cortex M3 的 CPU 来说,因为其内核的特点,在执行完中断服务函数后的返回指令与普通函数调用的返回指令是一样的,因此中断服务函数的写法与 C 语言中普通函数的定义没有区别,比如下面是 STM32F103 的一个外部中断的服务函数

    void EXTI0_IRQHandler(void){

    通过上述的代码我们可以看到中断服务函数的另一个特点,就是它的返回值和形参都为 void ,这也是由原因的,因为中断服务函数本来就不是由主程序进行调用的,既然中断服务函数不会被其他函数所调用,那么其返回值和形参自然是 void 了,要使得 CPU 能够找到中断服务子程序,那么这个函数的函数名不是随意命名的,比如这里的 EXTI0_IRQHandler,这个函数名与中断向量表中表项的值是对应起来的,因为函数名从数值上看代表的是函数的入口地址。
    上述说到是因为 ARM Cortex M3 的 CPU 在处理中断服务函数的返回地址时用的指令和普通函数调用时的返回地址的指令一致,所以才能够使中断服务函数的写法与普通 C 语言函数没有差异,下面举一个 51 单片机的定时器中断服务函数的例子:

    void InterruptTimer0() interrupt 1{

    上述的这个中断服务函数, InterruptTimer0可以任意命名,但是括号后面的是有严格规定的,为了 51 单片机能够进行中断处理,C51 编译器对函数进行了扩展,增加了一个扩展关键字interrupt,从而让 CPU 知道这个是一个中断服务函数。

    中断的嵌套

    C 语言函数能够进行嵌套调用,同样的中断服务函数也能够进行嵌套,同样的用一张图来表明中断的嵌套:

    6c0646f0a0f9db47c380fba2469473fd.png
    中断嵌套示意图

    可以看到中断的嵌套也是在消耗堆栈的,和使用函数嵌套调用一个道理,这里需要注意的是中断是存在优先级的,如果发生了一个比当前执行的中断优先级低的中断请求,那么新产生的中断请求会等待正在执行的中断执行完成之后才开始响应新的中断,如果产生的中断的优先级比当前的优先级要高,那么也就会像上图所示一样进行执行。另外需要注意的是,中断的优先级是有限的,也就是说中断嵌套的层数是有限的,如果再考虑堆栈溢出的话,那么中断嵌套的层数还和堆栈的大小有关。

    总结

    上述就是关于中断的相关内容,简单地叙述了中断是如何响应的,如何执行保护现场和恢复现场的操作,CPU 如何根据中断向量表找到对应的中断服务函数,以及中断的嵌套,这就是这次分享的全部内容啦~

    50ffbc81831d1e0aefbfe772a3386577.gif

    如果觉得我的文章对您有所帮助,欢迎点击在看鼓励一下呐~

    展开全文
  • 还请各位朋友能够给我指出来,我将不胜感激,谢谢~前言笔者在《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务程序又是如何被执行的呢?...

    笔者能力有限,如果文中出现错误的地方,还请各位朋友能够给我指出来,我将不胜感激,谢谢~

    前言

    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地阐述这个概念。

    中断的概念

    当 CPU 正在处理某件事情的时候,外部发生的某一事件请求 CPU 迅速去处理,于是,CPU 暂时中止当前的工作,转去处理所发生的事件。中断服务处理完该事件以后,再回到原来被中止的地方,继续原来的工作,这样的过程称之为中断,示意图如下:

    35868748ad181bd1a5a8f0239a4751ae.png
    中断执行示意图

    中断响应及处理过程

    回顾函数调用的过程,子程序由主程序进行调用,从而完成执行。但是中断服务子程序并没有被主程序进行调用,中断服务子程序的执行是通过中断请求完成的,也就是说中断服务子程序可以发生在主程序执行的随意位置,那现在就面临一个问题了,如果当CPU 正在执行函数调用的子程序的内容的时候产生了一个中断请求,那么这个时候 CPU 将暂停执行函数调用的子程序的内容,转而去执行中断服务子程序的内容,如果不进行额外的处理,那么函数调用的子程序的相关数据将丢失,因此在执行中断服务子程序之前,CPU 必须要保存发生中断的那个地方的相关信息,这个操作用专业的术语来讲就是保护现场,保护现场之后,CPU 将执行中断服务子程序的内容,执行完中断服务子程序的内容之后,CPU 要回到刚刚暂停的地方继续执行,另外在返回之前,CPU 还要进行恢复现场,恢复现场之后,就可以返回到暂停的地方继续执行了,下面是整个过程的示意图:

    7fef795bdf3fe0d3b2acfe27c85541da.png
    中断响应示意图

    通过上述示意图我们也可以看到在返回地址这个地方,中断服务子程序和函数调用子程序的返回地址所遵循的原理是一样的,函数调用子程序的返回地址是函数调用指令的下一条指令的地址,而在上述示意图中的 N 和 N+1 的含义也是类似的,当 CPU 执行到第 N 条指令的时候,CPU 接收到了一个中断请求,在执行完第 N 条指令之后,转而去执行中断服务子程序的内容,然后中断服务子程序的返回地址对应的是第 N+1 条指令的地址。

    中断的堆栈占用

    在刚刚所述的内容中,说到 CPU 在执行中断服务子程序的内容之前,需要保护现场,那保护现场这个操作具体是怎么实现的呢?这个时候,就要用到我们的堆栈了。在这里拿 ARM Cortex M3 举例,在响应中断时所做的第一个操作就是保护现场,它会依次把 xPSR,PC,LR,R12以及 R3-R0 由硬件自动压入适当的堆栈中,注意,这里是自动压入堆栈,也就是说如果我们看对应的汇编代码是看不到这部分压栈操作的。另外,我们知道对于 ARM Cortex M3 的堆栈指针来说,它存在两个,一个是主堆栈指针(MSP),一个是线程堆栈指针(PSP),其中主堆栈指针是复位后默认使用的堆栈指针,用于操作系统内核和中断处理程序,线程堆栈指针(PSP)是由用户的应用程序代码所使用。那么在执行现场保护时将相关寄存器的值压入堆栈,应该使用哪个堆栈指针呢?这也是存在一个原则的,如果在响应中断时,当前的代码正在使用线程堆栈(PSP),那么将使用线程堆栈指针(PSP)进行压栈,否则将使用主堆栈指针(MSP)。另外在 CPU 进入中断服务子程序之后,所涉及的堆栈操作所使用的堆栈一直是主堆栈指针(MSP)。为了更直观的展示这个过程,下图是发生中断请求后,堆栈的变化示意图:

    d55062e6124715fd82875ffcaba5c920.png
    中断堆栈调用示意图

    通过上图我们可以很清楚地看到在响应中断时产生的保护现场操作,堆栈明显增长了,而在执行完中断服务子程序的内容之后,又将执行恢复现场的操作,这个时候堆栈的内容又减少了。为了更清楚地展示压入堆栈寄存器的操作,笔者在这里也给出上述图中堆栈粉色部分的详细内容,图片如下:

    f30f1a088b4394859500ec21467cbe0a.png
    保护现场堆栈内容

    上述就是保护现场时所压入堆栈的相关寄存器,另外还需注意的一点是当所涉及的中断服务子程序逻辑比较复杂的时候,就需要更多的寄存器了,这个时候就需要用到 R4-R11 了,但是这部分寄存器是不能进行自动压栈的,也就是说如果在中断服务子程序中使用到这部分寄存器的时候就需要进行手动压栈,那么这部分的压栈操作在汇编层面就能看到了。

    中断向量表

    在上述所阐述的内容中,我们知道了中断会在主程序的任意发生中断请求,从而执行中断服务子程序的内容,也阐述了在执行中断服务子程序的内容之前需要进行保护现场的操作,以及执行完中断服务子程序的内容之后需要进行恢复现场。现在我们再来思考,在 CPU 中,中断源不止一种,可以是按键按下所触发的一个外部中断,也可能是在使用串行通信时,收到数据所触发的一个中断,亦或是在 CPU 中定义的一个定时中断由于设置的时间到了而触发的定时中断,这个时候,就浮现一个问题了,要如何将这一个一个的中断源与其各自的中断服务子程序所一一对应起来呢?换句更为通俗的话来讲就是当 CPU 接收到一个中断信号时,CPU 将如何找到对应的中断服务子程序进行执行呢?这个时候,就需要中断向量表了,下面是中断向量表的特点:

    • 中断向量表在 CPU 中是一段连续的存储空间

    • 中断向量表在 CPU 复位后有默认的起始地址

    • 每一个中断在中断向量表中都有对应的表项,该表项的值为该中断源对应的中断服务程序的地址

    • 由程序代码确定中断向量表中的每个表项

    上述特点说中断向量表都存在默认的起始地址,在这里依旧拿 ARM Cortex M3 内核来看,它的中断向量表默认的起始地址是从地址 0x0000 0000 开始的,如下图所示:

    fdb2711341594712634347f3edb508d2.png
    中断向量表

    当然这只是一部分,并不是全部的表项。有了中断向量表之后,那么当 CPU 接收到中断请求的时候,就会根据这个中断请求的信号去查这个表,从而查找到其所对应的中断服务子程序的地址,然后将这个地址赋值给 PC 指针寄存器就,那么 CPU 就可以完成中断服务子程序的执行了,对于 PC 指针寄存器不是太清楚地朋友可以看笔者的这篇文章 《程序是如何在 CPU 中运行的(二)》

    中断服务函数的写法

    中断服务函数的写法不同的 CPU 有各自不同的写法,对于 ARM Cortex M3 的 CPU 来说,因为其内核的特点,在执行完中断服务函数后的返回指令与普通函数调用的返回指令是一样的,因此中断服务函数的写法与 C 语言中普通函数的定义没有区别,比如下面是 STM32F103 的一个外部中断的服务函数

    void EXTI0_IRQHandler(void){
        /* 确保是否产生了中断 */
        if(EXTI_GetITStatus(EXTI_Line0) != RESET) 
        {   
            /*用户代码*/
            /*清除中断标志位*/
            EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
        }  
    }

    通过上述的代码我们可以看到中断服务函数的另一个特点,就是它的返回值和形参都为 void ,这也是由原因的,因为中断服务函数本来就不是由主程序进行调用的,既然中断服务函数不会被其他函数所调用,那么其返回值和形参自然是 void 了,要使得 CPU 能够找到中断服务子程序,那么这个函数的函数名不是随意命名的,比如这里的 EXTI0_IRQHandler,这个函数名与中断向量表中表项的值是对应起来的,因为函数名从数值上看代表的是函数的入口地址。
    上述说到是因为 ARM Cortex M3 的 CPU 在处理中断服务函数的返回地址时用的指令和普通函数调用时的返回地址的指令一致,所以才能够使中断服务函数的写法与普通 C 语言函数没有差异,下面举一个 51 单片机的定时器中断服务函数的例子:

    void InterruptTimer0() interrupt 1{
        /*省略*/
    }

    上述的这个中断服务函数, InterruptTimer0可以任意命名,但是括号后面的是有严格规定的,为了 51 单片机能够进行中断处理,C51 编译器对函数进行了扩展,增加了一个扩展关键字interrupt,从而让 CPU 知道这个是一个中断服务函数。

    中断的嵌套

    C 语言函数能够进行嵌套调用,同样的中断服务函数也能够进行嵌套,同样的用一张图来表明中断的嵌套:

    508a43d48f431c4cf5e375a304e23575.png
    中断嵌套示意图

    可以看到中断的嵌套也是在消耗堆栈的,和使用函数嵌套调用一个道理,这里需要注意的是中断是存在优先级的,如果发生了一个比当前执行的中断优先级低的中断请求,那么新产生的中断请求会等待正在执行的中断执行完成之后才开始响应新的中断,如果产生的中断的优先级比当前的优先级要高,那么也就会像上图所示一样进行执行。另外需要注意的是,中断的优先级是有限的,也就是说中断嵌套的层数是有限的,如果再考虑堆栈溢出的话,那么中断嵌套的层数还和堆栈的大小有关。

    总结

    上述就是关于中断的相关内容,简单地叙述了中断是如何响应的,如何执行保护现场和恢复现场的操作,CPU 如何根据中断向量表找到对应的中断服务函数,以及中断的嵌套,这就是这次分享的全部内容啦~

    ad51c1ca84ed20414189473d31445a42.gif

    如果觉得我的文章对您有所帮助,欢迎点击在看鼓励一下呐~

    展开全文
  • 中断服务程序是如何被执行的 ?

    千次阅读 2020-05-25 00:07:18
    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地阐述...

    前言

    笔者在 《程序是如何在 CPU 中运行的(二)》中从 PC 指针寄存器的角度分析了一级函数调用和二级函数调用执行的过程,那么中断服务子程序又是如何被执行的呢?两者的相同点和不同点是什么呢?该篇文章笔者将详细地阐述这个概念。

    中断的概念

    当 CPU 正在处理某件事情的时候,外部发生的某一事件请求 CPU 迅速去处理,于是,CPU 暂时中止当前的工作,转去处理所发生的事件。中断服务处理完该事件以后,再回到原来被中止的地方,继续原来的工作,这样的过程称之为中断,示意图如下:
    中断执行示意图

    中断响应及处理过程

    回顾函数调用的过程,子程序由主程序进行调用,从而完成执行。但是中断服务子程序并没有被主程序进行调用,中断服务子程序的执行是通过中断请求完成的,也就是说中断服务子程序可以发生在主程序执行的随意位置,那现在就面临一个问题了,如果当CPU 正在执行函数调用的子程序的内容的时候产生了一个中断请求,那么这个时候 CPU 将暂停执行函数调用的子程序的内容,转而去执行中断服务子程序的内容,如果不进行额外的处理,那么函数调用的子程序的相关数据将丢失,因此在执行中断服务子程序之前,CPU 必须要保存发生中断的那个地方的相关信息,这个操作用专业的术语来讲就是保护现场,保护现场之后,CPU 将执行中断服务子程序的内容,执行完中断服务子程序的内容之后,CPU 要回到刚刚暂停的地方继续执行,另外在返回之前,CPU 还要进行恢复现场,恢复现场之后,就可以返回到暂停的地方继续执行了,下面是整个过程的示意图:
    中断响应示意图
    通过上述示意图我们也可以看到在返回地址这个地方,中断服务子程序和函数调用子程序的返回地址所遵循的原理是一样的,函数调用子程序的返回地址是函数调用指令的下一条指令的地址,而在上述示意图中的 N 和 N+1 的含义也是类似的,当 CPU 执行到第 N 条指令的时候,CPU 接收到了一个中断请求,在执行完第 N 条指令之后,转而去执行中断服务子程序的内容,然后中断服务子程序的返回地址对应的是第 N+1 条指令的地址。

    中断的堆栈占用

    在刚刚所述的内容中,说到 CPU 在执行中断服务子程序的内容之前,需要保护现场,那保护现场这个操作具体是怎么实现的呢?这个时候,就要用到我们的堆栈了。在这里拿 ARM Cortex M3 举例,在响应中断时所做的第一个操作就是保护现场,它会依次把 xPSR,PC,LR,R12以及 R3-R0 由硬件自动压入适当的堆栈中,注意,这里是自动压入堆栈,也就是说如果我们看对应的汇编代码是看不到这部分压栈操作的。另外,我们知道对于 ARM Cortex M3 的堆栈来说,它存在两个,一个是主堆栈指针(MSP),一个是线程堆栈指针(PSP),其中主堆栈指针是复位后默认使用的堆栈指针,用于操作系统内核和中断处理程序,线程堆栈指针(PSP)是由用户的应用程序代码所使用。那么在执行现场保护时将相关寄存器的值压入堆栈,应该使用哪个堆栈指针呢?这也是存在一个原则的,如果在响应中断时,当前的代码正在使用线程堆栈指针(PSP),那么将使用线程堆栈指针(PSP)进行压栈,否则将使用主堆栈指针(MSP)。另外在 CPU 进入中断服务子程序之后,所涉及的堆栈操作所使用的堆栈一直是主堆栈指针(MSP)。为了更直观的展示这个过程,下图是发生中断请求后,堆栈的变化示意图:
    中断堆栈调用示意图
    通过上图我们可以很清楚地看到在响应中断时产生的保护现场操作,堆栈明显增长了,而在执行完中断服务子程序的内容之后,又将执行恢复现场的操作,这个时候堆栈的内容又减少了。
    为了更清楚地展示压入堆栈寄存器的操作,笔者在这里也给出上述图中堆栈粉色部分的详细内容,图片如下:
    保护现场堆栈内容
    上述就是保护现场时所压入堆栈的相关寄存器,另外还需注意的一点是当所涉及的中断服务子程序逻辑比较复杂的时候,就需要更多的寄存器了,这个时候就需要用到 R4-R11 了,但是这部分寄存器是不能进行自动压栈的,也就是说如果在中断服务子程序中使用到这部分寄存器的时候就需要进行手动压栈,那么这部分的压栈操作在汇编层面就能看到了。

    中断向量表

    在上述所阐述的内容中,我们知道了中断会在主程序的任意发生中断请求,从而执行中断服务子程序的内容,也阐述了在执行中断服务子程序的内容之前需要进行保护现场的操作,以及执行完中断服务子程序的内容之后需要进行恢复现场。现在我们再来思考,在 CPU 中,中断源不止一种,可以是按键按下所触发的一个外部中断,也可能是在使用串行通信时,收到数据所触发的一个中断,亦或者在 CPU 中定义的一个定时中断由于设置的时间到了而触发的定时中断,这个时候,就浮现一个问题了,要如何将这一个一个的中断源与其各自的中断服务子程序所一一对应起来呢?换句更为通俗的话来讲就是当 CPU 接收到一个中断信号时,CPU 将如何找到对应的中断服务子程序进行执行呢?这个时候,就需要中断向量表了,下面是中断向量表的特点:

    • 中断向量表在 CPU 中是一段连续的存储空间
    • 中断向量表在 CPU 复位后有默认的起始地址
    • 每一个中断在中断向量表中都有对应的表项,该表项的值为该中断源对应的中断服务程序的地址
    • 由程序代码确定中断向量表中的每个表项

    上述特点说中断向量表都存在默认的起始地址,在这里依旧拿 ARM Cortex M3 内核来看,它的中断向量表默认的起始地址是从地址 0x0000 0000 开始的,如下图所示:
    中断向量表
    当然这只是一部分,并不是全部的表项。有了中断向量表之后,那么当 CPU 接收到中断请求的时候,就会根据这个中断请求的信号去查这个表,从而查找到其所对应的中断服务子程序的地址,然后将这个地址赋值给 PC 指针寄存器就,那么 CPU 就可以完成中断服务子程序的执行了,对于 PC 指针寄存器不是太清楚地朋友可以看笔者的这篇文章 《程序是如何在 CPU 中运行的(二)》

    中断服务函数的写法

    中断服务函数的写法不同的 CPU 有各自不同的写法,对于 ARM Cortex M3 的 CPU 来说,因为其内核的特点,在执行完中断服务函数后的返回指令与普通函数调用的返回指令是一样的,因此中断服务函数的写法与 C 语言中普通函数的定义没有区别,比如下面是 STM32F103 的一个外部中断的服务函数

    void EXTI0_IRQHandler(void)
    {
    	/* 确保是否产生了中断 */
    	if(EXTI_GetITStatus(EXTI_Line0) != RESET) 
    	{	
    		/*用户代码*/
    		/*清除中断标志位*/
    		EXTI_ClearITPendingBit(KEY1_INT_EXTI_LINE);     
    	}  
    }
    

    通过上述的代码我们可以看到中断服务函数的另一个特点,就是它的返回值和形参都为 void ,这也是由原因的,因为中断服务函数本来就不是由主程序进行调用的,既然中断服务函数不会被其他函数所调用,那么其返回值和形参自然是 void 了。
    上述说到是因为 ARM Cortex M3 的 CPU 在处理中断服务函数的返回地址时用的指令和普通函数调用时的返回地址的指令一致,所以才能够使中断服务函数的写法与普通 C 语言函数没有差异,下面举一个 51 单片机的定时器中断服务函数的例子:

    void InterruptTimer0() interrupt 1
    {
        /*省略*/
    }
    

    上述的这个中断服务函数, InterruptTimer0可以任意起,但是括号后面的是有严格规定的,为了 51 单片机能够进行中断处理,C51 编译器对函数进行了扩展,增加了一个扩展关键字interrupt,从而让 CPU 知道这个是一个中断服务函数。

    中断的嵌套

    C 语言函数能够进行嵌套调用,同样的中断服务函数也能够进行嵌套,同样的用一张图来表明中断的嵌套:
    中断嵌套示意图
    可以看到中断的嵌套也是在消耗堆栈的,和使用函数嵌套调用一个道理,这里需要注意的是中断是存在优先级的,如果发生了一个比当前执行的中断优先级低的中断请求,那么新产生的中断请求会等待正在执行的中断执行完成之后才开始响应新的中断,如果产生的中断的优先级比当前的优先级要高,那么也就会像上图所示一样进行执行。另外需要注意的是,中断的优先级是有限的,也就是说中断嵌套的层数是有限的,如果再考虑堆栈溢出的话,那么中断嵌套的层数还和堆栈的大小有关。

    总结

    上述就是关于中断的相关内容,简单地叙述了中断是如何响应的,如何执行保护现场和恢复现场的操作,CPU 如何根据中断向量表找到对应的中断服务函数,以及中断的嵌套,这就是这次分享的全部内容啦~

    展开全文
  • 问:多个排队怎么区分优先级?答:先触发先排队的原则问:中断事件和程序有限制吗...答:有相应的返回指令,也可以用跳转指令问:中断程序里面是什么啊?答:中断程序里是对BOOL量的赋值问:TCP编程时服务器程序需要...

    问:多个排队怎么区分优先级?

    答:先触发先排队的原则

    问:中断事件和程序有限制吗?

    答:没有

    问:执行死循环时,那三个值一直为1吗?

    答:是一直为1,需要赋值为0才会变化

    问:这个死循环指令在做项目的时候有什么作用啊?

    答:当要与初始化程序隔开时

    问:进入死循环,这个中断程序怎么返回呢?

    答:有相应的返回指令,也可以用跳转指令

    问:中断程序里面是什么啊?

    答:中断程序里是对BOOL量的赋值

    问:TCP编程时服务器程序需要端口号,那客户机程序需要端口号吗?

    答:在进行通信时,服务器程序需要端口号,客户端里也需要设定与服务器通讯的相同端口号。比如打电话,对方号码是168,你是客户端打电话给对方(服务器)也必须拨打168这个号码。

    问:不明白strComp函数为什么逻辑等于0时正常执行程序?

    答:是这样子的,我们的!strComp这个函数规定,如果结果返回值如果是0~127,那么执行命令是正常的,如果等于-1,那意味着转换失败。我们这里是通过比较它的结果返回值来判断指令是否执行正常,根据判断结果是否开始执行程序命令。

    7c45e90b548b81a71dde7a43fababf69.png

    问:怎样把一台昆仑通泰触摸屏程序下到另一台相同型号无程序昆仑通泰触摸屏上?#触摸屏#

    答:首先先把其中一个有程序触摸屏的程序上传到电脑上,操作方式是点击触摸屏软件文件选项卡里面的上传工程功能进行程序回传,回传完毕以后,把电脑的回传程序下载到新的一台触摸屏里面即可。注意,回传的前提条件必须是以前的触摸屏在下载程序的时候勾选了原始工程文件,否则不能回传。

    问:如果触摸屏上面按钮点击灯亮了以后,那用户自动注销了,是不是灯就灭不了了啊?

    答:那肯定是的,其实这个我们可以想一下,用户登录了以后,肯定是操作完一定的任务以后,才会注销用户,如果你注销了用户又要别的任务了,重新登录用户即可,这个不影响实际操作。

    问:际当中,如果我们忘记了触摸屏设置的密码怎么知道密码是多少?

    答:是这样子的,实际中,我们肯定是有触摸屏的原始程序的,打开触摸屏的原始程序,重新把密码设置一下,下载到触摸屏中即可,无法通过其它方式获取。

    问:串口调试软件有使用视频讲解吗?

    答:技成官网,欧姆龙叨叨回放课程有

    问:pLC与触摸屏485通讯,用不用写通讯程序,比喻读写

    答:PLC写程序,屏对应地址获取

    问:老师输入输出,可以用i0.0。和q0.0吗?

    答:屏不能对应PLC实际输入地址,可以用中间继电器W对应,屏对应输出地址可以显示

    展开全文
  • 理解中断

    2020-08-10 15:40:13
    答:1、中断服务函数***不能传递参数和函数返回值***,因为不知道什么时候产生中断,不知道给谁返回值,中断是随机发生的,不会有value = interrupt_func();当然除了linux软中断,他可以知道调用者是谁。 2、中断的...
  • 而且当配置多个(例如四个)时,即使在中断服务程序里删除第四个按键的相关程序,仍然是只有第四个按键有效并且使其他按键执行操作……有没有大佬知道这是为什么嘛?急急急! 下面是中断服务程序代码 interrupt ...
  • 通俗的来讲,异常就是程序执行过程中发生的错误,有时候发生错误,整个程序可能会全部终止。 这时,就需要抛异常的方法来实现当次执行程序中断,而不是整个程序全部终止 二、几个常见异常 1、requests.exceptions....
  • 最近导师的项目中要应用到DSP F28335,由于之前一直对f28335的中断不是很理解,...当处理完这些任务之后,要继续刚才的处理,因此在执行中断服务程序时候,必须保存执行现场以确保在完成更高级别任务或指令时能够再
  • 在学习单片机的时候,中断是一个必须要理解的知识点,在CPU执行程序时,发生了某种内部或者外部的随机事件(中断源),引起CPU暂时中断正在运行的程序,转去执行中断服务程序或中断处理程序来处理发生的随机事...
  • 串口接收中断和连续发送的矛盾!

    千次阅读 2015-10-10 11:08:10
    暂时想到的原因:因为串口接收中断服务程序也是需要时间来执行的,所以试想一下: MCU的串口外设在接收到二个数据的时候,MCU的主核正在执行第一个数据触发的中断服务程序,所以第二个数据触发的中断函数并未执行!...
  • 中断、异常和系统调用

    千次阅读 2018-07-24 15:23:53
    只有内核可以执行特权指令,方便为应用程序提供服务。 2 在外设连接计算机时,为了能够让计算机系统能够对外界做出适当的反应,需要提供中断机制,使得当外设和计算机系统有交互的时候,计算机系统能够做出相应的...
  • * 启动BIOS。这个时候位于实模式下,加载中断向量和中断服务程序 * 加载操作系统内核并为保护模式做准备。这个时候操作系统一共加载了3部分代码:引导程序bootsect,内核代码setup,内核代码system模
  • 一、启动BIOS,准备实模式下的中断向量表和中断服务程序 【实模式】一个20位的存储器地址空间(1M),可以直接通过软件的方式访问BIOS以及周边硬件,没有硬件支持的分页机制和实时多任务的概念,CPU的开机状态都是...
  • 开源-展现在我们面前的是数以千计的代码,在那一行行的...但,归根结底,它是一个执行者:执行用户程序-所谓为用户服务执行中断-所谓为外设服务执行系统调用-所谓解放程序员(姑且说它为广大的程序员服务,因为程
  • 1.为什么开始启动计算机的时候执行的是BIOS代码而不是操作系统自身的代码?(P1) ...这就需要硬件主动加载0xffff0处的BIOS程序,由BIOS准备好中断向量表、中断服务程序,接着通过中断“int 0x19...
  • Proteus仿真—40个单片机初学程序.

    热门讨论 2009-04-13 13:00:56
    在用表格进行程序设计的时候,要用以下的指令来完成 (1). 利用MOV DPTR,#DATA16的指令来使数据指针寄存器指到表的开头。 (2). 利用MOVC A,@A+DPTR的指令,根据累加器的值再加上DPTR的值,就可以使...
  • STM32(Cortex-M3)启动过程

    千次阅读 2018-10-08 19:59:10
    启动时从绝对地址0x0800 0000开始执行复位中断程序,即...这样CPU复位后会自动从中断向量表中第二个32位数据中取出复位中断向量的入口地址,PC就跳转到中断服务程序。这也就是为什么调试的时候程序会直接跳到0x08...
  • STM32(Cortex-M3)启动过程(入口地址)

    千次阅读 2013-12-12 11:45:15
    ARM7和ARM9启动时从绝对地址0X00000000开始执行复位中断程序,即固定了复位后...这样CPU复位后会自动从中断向量表中第二个32位数据中取出复位中断向量的入口地址,PC就跳转到中断服务程序。 也就是为什么调试的时候程序
  • 国科大操作系统思考题答案总结

    千次阅读 2018-11-25 11:15:28
    1.为什么开始启动计算机的时候执行的是BIOS代码而不是操作系统自身的代码?(P1) ...这就需要硬件主动加载0xffff0处的BIOS程序,由BIOS准备好中断向量表、中断服务程序,接着通过中断“int 0x1...
  • ucosIII概述

    2020-07-22 20:13:58
    前台:中断服务程序 实时内核 在设计实时系统时,可以把系统功能划分为多个任务,每个任务只实施单一的功能,任务一般都是是循环。当任务在执行时,可以说这个任务在独占CPU的资源。实时内核,做的事情就是什么...
  • Greys 使用线上系统为何经常出错?数据库为何屡遭黑手?...Greys是一个JVM进程执行过程中的异常诊断工具,可以在不中断程序执行的情况下轻松完成问题排查工作。 目标群体 有时候突然一个问题反...
  • Java异常处理手册和最佳实践

    千次阅读 2015-10-21 15:26:48
    程序执行的时候,无论什么时候产生错误,都会创建一个Exception对象并且正常的程序流也会中断,JRE会尝试找到处理这个异常的处理者。一个异常对象包含了许多的调试信息,例如:方法层次、产生异常的行号、异常
  • 操作系统常见问题

    2020-10-05 15:48:45
    1.操作系统的用户态和核心态切换条件以及为什么要切换 进程在用户空间运行时,被称为进程的用户态,而陷入内核空间的时候,被称为进程的...产生异常时:当CPU执行运行在用户态下的程序时,发生了某些事先不可知的异常,
  • 1、 什么是Tmux 我们在linux服务器上的...但是网络有时候不稳定,可能在半夜会出现连接断掉的情况,一旦连接断掉,我们所执行程序也就中断,我们当然可以写一个脚本后台运行,但是还是不方便。那么有没有一种工具可
  • 任务按照什么(What)顺序执行(FIFO、LIFO、优先级)? 有多少个(How Many)任务能并发执行? 在队列中有多少个(How Many)任务在等待执行? 如果系统由于过载而需要拒绝一个任务,那么应该...
  • 当我向p[i]赋值的时候,我的程序崩溃了。 1.35 chara{[3]}="abc";是否合法? 1.36 我总算弄清楚函数指针的声明方法了,但怎样才能初始化呢? 1.37 能够初始化联合吗? 第2章 结构、联合和枚举 结构声明 2.1 ...
  • )打开"服务和应用程序",单击服务,然后打开并启动 Windows Management Instrumentation 服务。当服务重新启动时,将基于以下注册表项中所提供的信息重新创建这些文件: HKEY_LOCAL_...
  • 当我向 p[i] 赋值的时候, 我的程序崩溃了。 o 2.14 我总算弄清除函数指针的声明方法了, 但怎样才能初始化呢? * 3. 结构、联合和枚举 o 3.1 声明 struct x1 { ...}; 和 typedef struct { ...} x2; 有什么不同...
  • 当我向p[i] 赋值的时候,我的程序崩溃了。 19  1.35 char a{[3]}= abc; 是否合法? 20 1.36 我总算弄清楚函数指针的声明方法了,但怎样才能初始化呢? 20 1.37 能够初始化联合吗? 20 第2章 结构、联合和...

空空如也

空空如也

1 2 3 4
收藏数 63
精华内容 25
关键字:

中断服务程序什么时候执行