- 程序清单L9.5给出的代码大部分与OSCtxSw()的代码相同,不同之处是,第一,由于中断已经发生,此处不需要再保存CPU寄存器(没有PUSHA, PUSH ES, 或PUSH DS);第二,OSIntCtxSw()需要调整堆栈指针,去掉堆栈中一些不需要的内容,以使堆栈中只包含任务的运行环境。图F9.5可以帮助读者理解这一过程。
- 程序清单L 9.5 OSIntCtxSw().
- _OSIntCtxSw PROC FAR
- ; ; Ignore calls to OSIntExit and OSIntCtxSw
- ; ADD SP,8 ; (Uncomment if OS_CRITICAL_METHOD is 1, see OS_CPU.H) (1)
- ADD SP,10 ; (Uncomment if OS_CRITICAL_METHOD is 2, see OS_CPU.H)
- ;
- MOV AX, SEG _OSTCBCur ; 载入DS
- MOV DS, AX
- ;
- LES BX, DWORD PTR DS:_OSTCBCur ; OSTCBCur->OSTCBStkPtr = SS:SP (2)
- MOV ES:[BX+2], SS
- MOV ES:[BX+0], SP
- ;
- CALL FAR PTR _OSTaskSwHook (3)
- ;
- MOV AX, WORD PTR DS:_OSTCBHighRdy+2 ; OSTCBCur = OSTCBHighRdy (4)
- MOV DX, WORD PTR DS:_OSTCBHighRdy
- MOV WORD PTR DS:_OSTCBCur+2, AX
- MOV WORD PTR DS:_OSTCBCur, DX
- ;
- MOV AL, BYTE PTR DS:_OSPrioHighRdy ; OSPrioCur = OSPrioHighRdy (5)
- MOV BYTE PTR DS:_OSPrioCur, AL
- ;
- LES BX, DWORD PTR DS:_OSTCBHighRdy ; SS:SP = OSTCBHighRdy->OSTCBStkPtr (6)
- MOV SS, ES:[BX+2]
- MOV SP, ES:[BX]
- ;
- POP DS ; 载入新任务的CPU环境 (7)
- POP ES (8)
- POPA (9)
- ;
- IRET ; 返回新任务 (10)
- ;
- _OSIntCtxSw ENDP
- <img src="https://img-blog.csdn.net/20170517091158576?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvRnVua3lGcm9nODIxOTUxMjU5/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""><pre code_snippet_id="2398550" snippet_file_name="blog_20170517_2_5958581" name="code" class="cpp">当中断发生后,CPU在完成当前指令后,进入中断处理过程。首先是保存现场,将返回地址压入当前任务堆栈,然后保存状态寄存器的内容。接下来CPU从中断向量处找到中断服务程序的入口地址,运行中断服务程序。在µC/OS-II中,要求用户的中断服务程序在开头保存CPU其他寄存器的内容[图F9.5(1)]。此后,用户必须调用OSIntEnter()或着把全局变量OSIntNesting加1。此时,被中断任务的堆栈中保存了任务的全部运行环境。在中断服务程序中,有可能引起任务就绪状态的改变而需要任务切换,例如调用了OSMboxPost(), OSQPostFront(),OSQPost(),或试图唤醒一个优先级更高的任务(调用OSTaskResume()),还可能调用 OSTimeTick(), OSTimeDlyResume()等等。
- µC/OS-II要求用户在中断服务程序的末尾调用OSIntExit(),以检查任务就绪状态。在调用OSIntExit()后,返回地址会压入堆栈中[图F9.5(2)]。
- 进入OSIntExit()后,由于要访问临界代码区,首先关闭中断。由于OS_ENTER_CRITICAL()可能有不同的操作(见9.03.02节),状态寄存器SW的内容有可能被压入堆栈[图F9.5(3)]。如果确实要进行任务切换,指针OSTCBHighRdy将指向新的就绪任务的OS_TCB,OSIntExit()会调用OSIntCtxSw()完成任务切换。注意,调用OSIntCtxSw()会在再一次在堆栈中保存返回地址[图F9.5(4)]。在进行任务切换的时候,我们希望堆栈中只保留一次中断发生的任务环境(如图F9.5(1)),而忽略掉由于函数嵌套调用而压入的一系列返回地址(图F9.5(2),(3),(4))。忽略的方法也很简单,只要把堆栈指针加一个固定的值就可以了[图F9.5(5)/程序清单L9.5(1)]。如果用方法2实现OS_ENTER_CRITICAL(),这个固定值是10;如果用方法1,则是8。实际操作中还与编译器以及编译模式有关。例如,有些编译器会为OSIntExit()在堆栈中分配临时变量,这都会影响具体占用堆栈的大小,这一点需要提醒用户注意。
- 一但堆栈指针重新定位后,就被保存到将要被挂起的任务OS_TCB中[图F9.5(6)/程序清单L9.5(2)]。在µC/OS-II中(包括µC/OS),OSIntCtxSw()是唯一一个与编译器相关的函数,也是用户问的最多的。如果您的系统移植后运行一段时间后就会死机,就应该怀疑是OSIntCtxSw()中堆栈指针重新定位的问题。
- 当当前任务的现场保存完毕后,用户定义的对外接口函数OSTaskSwHook()会被调用[程序清单L9.5(3)]。注意到OSTCBCur指向当前任务的OS_TCB,OSTCBHighRdy指向新任务的OS_TCB。在函数OSTaskSwHook()中用户可以访问这两个任务的OS_TCB。如果不用对外接口函数,请在头文件中关闭相应的开关选项,提高任务切换的速度。
- 从对外接口函数OSTaskSwHook()返回后,由于任务的更替,变量OSTCBHighRdy被拷贝到OSTCBCur中[程序清单L9.5(4)],同样,OSPrioHighRdy被拷贝到OSPrioCur中[程序清单L9.5(5)]。此时,OSIntCtxSw()将载入新任务的CPU环境,首先从新任务OS_TCB中取出SS和SP寄存器的值[图F9.5(7)/程序清单L9.5(6)],然后运行POP DS [图F9.5(8)/程序清单L9.5(7)], POP ES [图F9.5(9)/程序清单L9.5(8)], POPA[图F9.5(10)/程序清单L9.5(9)]取出其他寄存器的值,最后用中断返回指令IRET [图F9.5(11)/程序清单L9.5(10)]完成任务切换。
- 需要注意的是在运行OSIntCtxSw()和用户定义的OSTaskSwHook()函数期间,中断是禁止的。</pre><br>
- 图F 9.5 中断级任务切换时的80x86堆栈结构
- <pre></pre>
- <br>
0518
最新推荐文章于 2022-05-18 09:05:30 发布