uc/os-ii任务调度(一)
来源:互联网 发布:罗杰疑案知乎 编辑:程序博客网 时间:2024/05/20 20:19
多任务操作系统的核心工作就是任务调度。所谓调度,就是通过一个算法在多个任务中确定该运行的任务,做这项工作的函数就叫做调度器。μC/OS-II进行任务调度的思想是 “近似地每时每刻总是让优先级最高的就绪任务处于运行状态” 。为了保证这一点,μC/OS-II在系统或用户任务调用系统函数及执行中断服务程序结束时总是调用调度器,来确定应该运行的任务并运行它 。
μC/OS-Ⅱ的调度器主要有两个功能:一是确定进入就绪态的任务中哪个优先级最高;二是进行任务切换。调度有两种方式:任务级的调度是由OSSched()
函数完成的;中断级的调度是由OSIntExt()
函数完成的。
- 下面先分析任务级的调度函数
OSSched()
(基于ARM Cortex-M3)。
void OS_Sched (void){#if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0;#endif OS_ENTER_CRITICAL(); if (OSIntNesting == 0) { /* Schedule only if all ISRs done and ... */ if (OSLockNesting == 0) { /* ... scheduler is not locked */ OS_SchedNew(); if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];#if OS_TASK_PROFILE_EN > 0 OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */#endif OSCtxSwCtr++; /* Increment context switch counter */ OS_TASK_SW(); /* Perform a context switch */ } } } OS_EXIT_CRITICAL();}
(1) 采用第三种方法开关中断。OSSched()
函数的所有代码都属临界区代码。为了防止在处理的过程中有中断打入而将某些任务转入就绪状态,中断必须关闭。
(2) 若调度是禁止的(即调度上锁),或当前处于中断服务子程序中,任务的调度是不允许的,任务调度函数OSSched()
将退出。调度锁定嵌套计数器OSLockNesting
用于跟踪调度上锁次数,当OSLockNesting>0
时,表明调度是禁止的;只有OSLockNesting=0
时,调度才是允许的,也称为调度开锁。中断嵌套计数器OSIntNesting
用于跟踪中断嵌套层数,只要一进入中断服务,就有OSIntNesting>0
;只有所有中断都退出时,才有OSIntNesting = 0
。
(3) 若调度是开放的且程序不处于中断服务之中,则调用OS_SchedNew()
函数找出准备就绪且优先级最高的任务。
(4) 若准备就绪的最高优先级任务不是当前正在运行的任务,就要进行任务切换。为实现任务切换,必须切换任务控制块,将OSTCBHighRdy
指向优先级最高的那个任务控制块OS_TCB。并且计数器OSCtxSwCtr
加1,以跟踪任务切换次数。
(5)任务切换是通过一个宏调用OS_TASK_SW()
来完成的。任务切换很简单,实际上就是人为地模拟一次中断过程。它分两步完成,首先将被挂起的任务的CPU寄存器推入堆栈,然后将准备就绪的最高优先级任务的寄存器值从任务栈中恢复到寄存器中。在μC/OS-Ⅱ中,任务的栈结构就是模拟中断栈结构,所有CPU寄存器都保存在栈中,切换任务就像中断返回,不同的是:中断返回弹出的是被中断任务的CPU寄存器值,而任务切换弹出的是准备就绪的最高优先级任务的CPU寄存器值,这样准备就绪的高优先级任务就可以运行了。
以下这里重点分析一下OS_TASK_SW()
函数在ARM Cortex-M3处理器上的实现,OS_TASK_SW()
是汇编函数OSCtxSw()
的宏定义
#define OS_TASK_SW() OSCtxSw()
如下是OSCtxSw()
的汇编源码,该段代码的作用是触发一次PendSV异。任务切换实际上是在PendSV的异常服务程序中完成的。
OSCtxSw LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] BX LR
PendSV的异常服务程序汇编源码如下
OS_CPU_PendSVHandler CPSID I ; Prevent interruption during context switch MRS R0, PSP ; PSP is process stack pointer CBZ R0, OS_CPU_PendSVHandler_nosave ; Skip register save the first time SUBS R0, R0, #0x20 ; Save remaining regs r4-11 on process stack STM R0, {R4-R11} LDR R1, =OSTCBCur ; OSTCBCur->OSTCBStkPtr = SP; LDR R1, [R1] STR R0, [R1] ; R0 is SP of process being switched out ; At this point, entire context of process has been savedOS_CPU_PendSVHandler_nosave PUSH {R14} ; Save LR exc_return value LDR R0, =OSTaskSwHook ; OSTaskSwHook(); BLX R0 POP {R14} LDR R0, =OSPrioCur ; OSPrioCur = OSPrioHighRdy; LDR R1, =OSPrioHighRdy LDRB R2, [R1] STRB R2, [R0] LDR R0, =OSTCBCur ; OSTCBCur = OSTCBHighRdy; LDR R1, =OSTCBHighRdy LDR R2, [R1] STR R2, [R0] LDR R0, [R2] ; R0 is new process SP; SP = OSTCBHighRdy->OSTCBStkPtr; LDM R0, {R4-R11} ; Restore r4-11 from new process stack ADDS R0, R0, #0x20 MSR PSP, R0 ; Load PSP with new process SP ORR LR, LR, #0x04 ; Ensure exception return uses process stack CPSIE I BX LR ; Exception return will restore remaining context END
uC/OS-II采用软中断指令触发PendSV异常来实现任务的切换。
1. 获取当前任务的堆栈指针PSP
。如果PSP=0
,判断为第一次任务切换,跳至OS_CPU_PendSVHandler_nosave
;如果PSP!=0
,继续执行步骤2。如果任务第一次运行,则PSP=0这里写代码片
;如果不是第一运行,则PSP=ptos-0x20
。因为CM3在进入PendSV异常服务函数时会自动将xPSR、PC、LR、R12、R3--R0
依次保存金PSP=ptos
所指向的堆栈。上述寄存器入栈后PSP=ptos-#0x20
。
2. 将R11--R4
压入当前任务堆栈。PSP=ptos-#0x40
。
3. 将PSP
保存到当前任务控制块中OSTCBCur->OSTCBStkPtr = PSP
。
以下为OS_CPU_PendSVHandler_nosave
4. 调用钩子函数OSTaskSwHook();
。
5. 获得待运行任务的任务控制块OSTCBCur = OSTCBHighRdy;
。
6. PSP
指向待运行任务中保存的堆栈地址PSP=STCBHighRdy->OSTCBStkPtr;
执行后PSP=ptos-0x40;
。
7. 将R4--R11
依次出栈。PSP=ptos-#0x20
。
8. 异常返回,返回时将R0--R3、R12、LR、PC、xPSR
依次出栈。
======= 任务级任务调度就分析这么多吧!!!========
下面分析程序如何开始运行第一个任务,其中包括PendSV异常优先级设定的汇编代码,PendSV优先级设置为最低0xFF。
uC/OS-II一般在OSStart()
前先创建一个启动任务,但是这个任务并没有运行,因为OSRunning == OS_FALSE
。执行OSStart()
触发第一次任务调度让系统有任务跑起来。
void OSStart (void){ if (OSRunning == OS_FALSE) { OS_SchedNew(); /* Find highest priority's task priority number */ OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ }}
执行OSStartHighRdy();
相当于一次任务调度。
1. 设置PendSV异常优先级为最低0xFF。
2. 初始化PSP=0;
,标志第一次执行任务调度。
3. 设置OSRunning = TRUE
。
4. 触发一次PendSV异常,执行PendSV异常服务程序OS_CPU_PendSVHandler
OSStartHighRdy LDR R0, =NVIC_SYSPRI14 ; Set the PendSV exception priority LDR R1, =NVIC_PENDSV_PRI STRB R1, [R0] MOVS R0, #0 ; Set the PSP to 0 for initial context switch call MSR PSP, R0 LDR R0, =OSRunning ; OSRunning = TRUE MOVS R1, #1 STRB R1, [R0] LDR R0, =NVIC_INT_CTRL ; Trigger the PendSV exception (causes context switch) LDR R1, =NVIC_PENDSVSET STR R1, [R0] CPSIE I ; Enable interrupts at processor levelOSStartHang B OSStartHang ; Should never get here
- uc/os-ii任务调度(一)
- uc/os-ii任务调度(二)
- uC/OS-II任务就绪表及任务调度
- UC/OS-II中的任务(一)
- uC/OS-II的任务调度与时钟
- 嵌入式上UC/OS - II使用注意点------任务调度
- uC/OS-II任务调度中判断最高优先级
- uC/OS-II的任务管理和调度
- uC/OS-II的任务调度和时钟
- uc/os-ii任务调度的锁定与解锁
- 2、uc/os:ucos-ii的任务调度机制
- uC/OS-II的任务管理和调度
- uC/OS-II学习心得与改进(一) uC/OS-II的任务切换总结
- uC/OS-II的任务
- uC/OS-II 中的任务
- uc/os-ii统计任务
- uc/os-ii删除任务
- uc/os-ii任务延时
- Python学习笔记 - 迭代器Iterator
- .net是什么 都说.net是平台 平台是什么意思? java不是一种语言吗 怎么又拿来和.net比较?
- 一个被 -webkit-box 坑了的娃
- 关于SAP中的记账码的解释
- 欢迎使用CSDN-markdown编辑器
- uc/os-ii任务调度(一)
- Linux中基本I/O 重定向的符号及其用法和文件标识符
- leetcode刷题,总结, 记录,备忘 12
- 复制代码片段
- [VC++]_[初级]_[使用zlib标准库解压zip文件]
- ORA-01274: cannot add datafile '/oradata/orauat/tbs_captain01.dbf'
- OOP
- Hadoop中的进程与Mapper实例,Reducer实例
- VS中语言属性对字符集转码的影响