uCOSII内核架构解析.docx
《uCOSII内核架构解析.docx》由会员分享,可在线阅读,更多相关《uCOSII内核架构解析.docx(26页珍藏版)》请在冰豆网上搜索。
uCOSII内核架构解析
嵌入式RTOS
1.嵌入式系统基本模型
2.RTOS设计原则
采用各种算法和策略,始终保持系统行为的可预测性。
即在任何情况下,在系统运行的任何时刻,OS的资源配置策略都能为争夺资源(包括CPU、内存、网络带宽等)的多个实时任务合理地分配资源,使每个实时任务的实时性要求都能得到满足。
3.GPOS与RTOS
ØGPOS:
注重每次执行的平均响应时间,而不是某次特定执行的响应时间。
ØRTOS:
除满足应用功能需求外,还要满足实时性要求,始终保证系统行为的可预测性(predictability)。
与GPOS不同,RTOS注重的不是系统的平均表现,而是要满足每个实时任务在最坏情况下的实时性要求。
也就是说,RTOS注重的是个体表现,更准确地说是个体最坏情况表现。
RTOS与GPOS的差别主要表现在:
a)任务调度策略不同;
b)内存管理方式不同;
c)中断处理方式不同;
d)系统管理方式不同;
4.嵌入式开发模式
Ø单片机系统的前后台程序:
不使用OS,将应用程序设计成死循环,系统轮流处理各事件,对时间响应要求高的异步事件采用中断进行处理。
Ø基于任务(进程)的软件设计方法:
使用OS,由OS管理硬件资源,任务只是在需要资源时申请即可,至于when/which,完全由OS决定。
5.(不)可重入
(1)可重入函数:
指函数代码在运行过程中可以被中断,中断返回后仍能够恢复到原来的状态,并能准确无误执行的函数。
可重入函数可以被一个以上的任务调用,而不必担心数据被破坏。
可重入函数或者只使用局部变量,即变量保存在CPU寄存器或堆栈中;或者使用全局变量,则要对全局变量予以保护。
(2)不可重入函数:
函数在运行过程中不可以被中断。
6.互斥条件
实现任务间通信最简便的办法是使用共享数据,但要保证任务在处理共享数据时的排它性。
使共享资源满足互斥条件,最一般的方法有:
(1)关中断
使用某种实时内核,一般情况下关中断的时间最长不超过内核本身的关中断时间,这样就不会影响系统中断延迟。
(2)使用测试并置位指令
Test&Set操作可能是微处理器一条不会被中断的指令,否则应该在程序中关中断做TAS操作再开中断。
(3)禁止做任务切换
此时任务切换虽然是禁止的,但仍允许中断。
如果这时中断来了,ISR会在这一临界区内立即执行。
(4)利用信号量;
7.临界状态
临界状态指当前程序处于不可中断状态。
一般情况下,在调用不可重入函数前或在修改全局变量数据时,都需要先进入临界状态。
进入临界状态的主要操作是关闭所有可以屏蔽的中断;而退出临界状态的主要操作是恢复到上次进入临界状态时前中断管理的状态。
在uC/OS-II中,宏OS_ENTER_CRITICAL()描述进入临界状态所完成的操作,宏OS_EXIT_CRITICAL()描述退出临界状态的操作。
uC/OS-II提供了3种进入和退出临界状态的办法,可以根据CPU类型由宏OS_CRITICAL_MOTHOD指定具体的临界状态处理办法。
uC/OS-II基本介绍
1.uC/OS-II文件结构
2.uC/OS-II组成部分
uC/OS-II大致可以分成系统核心(包含任务调度)、任务管理、时间管理、多任务同步与通信、内存管理、CPU移植等部分。
(1)核心部分(OSCore.c):
uC/OS-II处理核心,包括初始化、启动、中断管理、时钟中断、任务调度及事件处理等用于系统基本维持的函数。
(2)任务管理(OSTask.c):
包含与任务操作密切相关的函数,包括任务建立、删除、挂起及恢复等,uC/OSII以任务为基本单位进行调度。
(3)时钟部分(OSTime.c):
uC/OS-II中最小时钟单位是timetick(时钟节拍),其中包含时间延迟、时钟设置及时钟恢复等与时钟相关的函数。
(4)多任务同步与通信(OSMbox.c,OSQ.c,OSSem.c,OSMutex.c,OSFlag.c):
包含事件管理函数,涉及Mbox、msgQ、Sem、Mutex、Flag等。
(5)内存管理部分(OSMem.c):
主要用于构建私有的内存分区管理机制,其中包含创建memPart、申请/释放memPart、获取分区信息等函数。
(6)CPU接口部分:
uC/OS-II针对特定CPU的移植部分,由于牵涉到SP等系统指针,通常用汇编语言编写,包括任务切换、中断处理等内容。
3.uC/OS-II任务状态
在uC/OS-II中,一个任务就是一个线程,该任务可以认为CPU完全属于它自己。
任务有自己的堆栈和CPU寄存器,并且被赋予一定的优先级。
任务可能处于睡眠、就绪、运行、等待或中断服务状态之一。
uC/OS-II系统核心
主要包含在C源文件OS_CORE.C中。
1.uC/OS-II任务调度
(1)uC/OS-II调度算法
uC/OS-II采用基于优先级的调度算法,总是选择当前处于就绪状态的优先级最高的任务进行调度。
uC/OS-II是可抢占性的强实时性OS,在完成中断后允许进行新的任务调度。
uC/OS-II有两种调度方式:
任务级任务调度、中断级任务调度。
(2)任务就绪表
INT8UconstOSUnMapTbl[256]={…};
OS_EXTINT8UOSRdyGrp;
OS_EXTINT8UOSRdyTbl[OS_RDY_TBL_SIZE];
Ø添加就绪任务至就绪表;
Ø从就绪表删除就绪任务;
Ø查找最高优先级就绪任务OS_SchedNew();
(3)任务级任务调度
指在非中断返回时进行任务调度,一般发生在当前任务因时间延迟或等待某事件而阻塞或被挂起,或有更高优先级的任务处于就绪状态。
任务的基本信息:
ØCPU的PC寄存器:
任务当前执行的位置;
ØCPU的通用寄存器:
任务当前执行涉及的临时数据;
ØCPU的状态寄存器:
存储当前CPU的状态。
任务级任务切换:
从一个任务直接切换至另一个任务,不涉及CPU状态的切换,OS_TASK_SW()既保存当前任务上下文,又恢复新任务上下文。
过程:
OS_Sched()->OS_SchedNew()->OS_TASK_SW()
(4)中断级任务调度
中断级任务切换:
在中断处理完成后,通过OSIntExit()判断是否有更高优先级就绪任务。
如果有,调用OSIntCtxSW()恢复新任务上下文。
注意:
在中断处理中,已经保存了被中断任务的上下文,所以这里仅仅恢复。
过程:
OSIntExt()–>OSIntEnter()->ISR–>OSIntExit()->OSIntCtxSW()
(5)调度器上锁与解锁
uC/OS-II提供调度器锁定功能,在锁定期间不能进行任务调度。
uC/OS-II使用全局变量OSLockNesting标识是否锁定了任务调度器。
ØOS_EXTINT8UOSLockNesting;
ØvoidOSSchedLock(void);
ØvoidOSSchedUnlock(void);
(6)中断管理函数
在中断处理中,不允许进行任务管理、事件管理及任务调度等操作。
uC/OS-II通过全局变量OSIntNesting标识当前是否处于中断状态。
在所有任务及事件管理的程序中,都有对OSIntNesting进行判断的语句。
ØvoidOSIntEnter(void);
ØvoidOSIntExit(void);
(7)中断相关问题
ØOS_ENTER_CRITICAL()
ØOS_EXIT_CRITICAL()
关中断使得uC/OS-II能够同时避免有其他任务或中断服务进入临界代码段。
调用uC/OS-II功能函数时,中断总应当是开着的。
ØuC/OS-II如何禁止调度?
Ø在中断中允许调度吗?
为什么?
ØuC/OS-II如何屏蔽中断?
2.uC/OS-II系统启动
uC/OS-II首先调用OSInit()进行初始化,然后创建任务(此时还未启动系统,仅仅为其分配资源),然后调用OSStart()启动系统,将CPU控制权交给uC/OS-II,OS根据任务优先级选择由哪个任务开始执行,或创建新的任务。
(1)初始化函数OSInit()
OSInit()主要完成初始化操作,包括初始化全局变量(在OS_InitMisc()中)、任务就绪表、TCB、ECB、FCB、内存单元、消息队列,并创建空闲任务。
如果有必要,创建统计任务。
ØOS_InitMisc();//初始化部分全局变量
ØOS_InitRdyList();//初始化任务就绪表
ØOS_InitTCBList();//初始化空闲TCB链表
ØOS_InitEventList();//初始化ECB链表
ØOS_FlagInit();//初始化事件组标志结构
ØOS_MemInit();//初始化内存管理
ØOS_QInit();//初始化消息队列
ØOS_InitTaskIdle();//创建空闲任务
ØOS_InitTaskStat();//创建统计任务
uC/OS-II初始化了5个空的数据结构缓冲区,每个缓冲区都是单向链表,允许uC/OS-II从缓冲区中迅速取得或释放一个缓冲区中的元素。
(2)启动函数OSStart()
OSStart()在一切准备就绪且需要首先创建的任务都被创建后,启动uC/OS-II。
它从就绪表中查找最高优先级就绪任务,并恢复其上下文开始执行。
过程:
OSStart()->OS_SchedNew()->OSStartHighRdy()
问题:
任务第一次被调用时,哪来的上下文供其恢复呢?
创建任务时,调用了OSTaskStkInit()初始化任务堆栈,可此函数中没有涉及任务的上下文呀?
(3)统计任务OSTaskStat
OSTaskStat用于计算CPU利用率。
设置OS_CFG.H中的OS_TASK_STAT_EN为1,创建统计任务,在系统启动后一直处于就绪状态。
刚开始时,空闲任务运行1S,为计算CPU利用率提供一个基准值,并保存在统计任务的堆栈中,这个值不会改变除非重新启动CPU。
此后空闲任每次被其它任务抢去CP
U时,它里面的计数器就会直接记录下CPU空闲的时间。
3.uC/OS-II系统时钟
任何实时系统的时钟硬件设备每隔一段时间(一个系统tick)产生一个硬件中断,OS接收到该中断后,更新时间计数器,更新所有对时钟依赖的程序代码,从而维持系统有序稳定的运行。
主要包含在C源文件OS_TIME.C中。
Ø#defineOS_TICKS_PER_SEC100//系统时钟中断间隔
ØOS_EXTvolatileINT32UOSTime;//系统运行的时间值
ØvoidOSTimeTick(void);//时钟中断服务程序
ØvoidOSTimeDly(INT16Uticks);//延迟指定时钟节拍
ØINT8UOSTimeDlyHMSM(…);//延迟指定时间长度
ØINT8UOSTimeDlyResume(prio);//恢复等待(时延/阻塞)任务
ØINT32UOSTimeGet(void);//读取当前时间
ØvoidOSTimeSet(INT32Uticks);//设置当前时间
4.uC/OS-II事件管理
(1)事件控制块
ØINT8UOSEventType;//事件类型
Øvoid*OSEventPtr;//指向MBox或Queue
ØINT16UOSEventCnt;//信号量计数器(注:
Mutex)
ØINT8UOSEventGrp;//事件等待组标志
ØINT8UOSEventTbl[];//时间任务等待表
ØINT8UOSEventName[];//时间名称
(2)ECB管理机制
ØOS_EXTOS_EVENT*OSEventFreeList;//空闲ECB链表指针
ØOS_EXTOS_EVENTOSEventTbl[];//ECB结构体数组
(3)ECB管理函数
ØOS_InitEventList():
初始化ECB;
ØOS_EventWaitListInit():
在创建事件时调用,初始化ECB任务等待表;
ØOS_EventTaskRdy():
在事件发生时调用,修改此事件中最高优先级任务的TCB成员变量,在任务就绪表中添加此任务,将相应信息传递给该任务TCB,并将此任务从事件的任务等代表中删除;
ØOS_EventTaskWait():
在申请资源失败而暂停当前任务时被调用,将任务从任务就绪表中删除,并添加到事件的任务等代表中;
ØOS_EventTO():
在事件等待超时时调用,将此任务从事件的任务等代表中删除,并修改该任务的TCB成员变量;
uC/OS-II任务管理
1.C可执行代码结构
Ø代码段.text:
存放CPU执行的机器指令,通常.text是可共享且只读的。
Ø数据段.data:
.rodata(常量数据)、.rwdata(已初始化全局变量、静态变量)。
Ø未初始化数据段.bss:
未初始化的全局变量、静态变量。
Ø栈.stack:
存放函数参数、局部变量及任务切换时的上下文。
Ø堆.heap:
用于动态内存分配。
2.任务结构
在uC/OS-II中,任务是事件运行和管理的基本单元。
一个uC/OS-II任务至少包含程序代码、栈和TCB,还可选择性使用相当于堆的动态内存空间。
程序运行时,uC/OS-II中的任务相当于可执行代码(可单独运行的单元)。
uC/OS-II任务的各部分如何管理?
?
?
3.任务栈
Ø任务栈数据类型:
typedefunsignedshortOS_STK
Ø任务栈增长方向:
#defineOS_STK_GROWTH0/1
Ø任务栈基本功能:
?
?
?
4.任务控制块
(1)TCB描述
主要用来存储任务的当前属性。
(问:
任务第一次被调度时,该如何运行?
)
(2)TCB主要成员
Ø任务栈空间位置:
OSTCBStkPtr、OSTCBStkBottom、OSTCBStkSize;
Ø任务通信与同步:
OSTCBEventPtr、OSTCBMsg;
Ø任务事件组标志:
OSTCBFlagNode、OSTCBFlagsRdy;
Ø任务等待/阻塞:
OSTCBDly、OSTCBPendTO;
Ø任务当前状态:
OSTCBStat;
Ø任务优先级:
OSTCBPrio;
(3)TCB全局变量
ØOS_EXTOS_TCBOSTCBTbl[OS_MAX_TASKS+OS_N_SYS_TASKS];
ØOS_EXTOS_TCBOSTCBPrioTbl[OS_LOWEST_PRIO+1];
ØOS_EXTOS_TCB*OSTCBFreeList;
ØOS_EXTOS_TCB*OSTCBList;
ØOS_EXTOS_TCB*OSTCBHighRdy;
ØOS_EXTOS_TCB*OSTCBCur;
5.任务状态切换
6.任务管理函数
OS_TASK.C:
11个函数
ØOSTaskCreate();
ØOSTaskCreateExt();
ØOSTaskDel();
ØOSTaskDelReq();
ØOSTaskChangePrio();
ØOSTaskSuspend();
ØOSTaskResume();
ØOSTaskNameGet();
ØOSTaskNameSet();
ØOSTaskStkChk();
ØOS_TaskStkClr();
ØOSTaskQuery();
uC/OS-II通信与同步
1.消息邮箱Mbox
Mbox用于多任务间单一消息的传递,uC/OS-II使用ECB管理Mbox的基本信息,OSEventPtr指向创建Mbox时指定的内存空间。
事件的创建由具体的事件管理程序实现。
主要包含在C源文件OS_MBOX.C中。
ØOS_EVENT*OSMboxCreate(void*msg);
Øvoid*OSMboxPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
Øvoid*OSMboxAccept(OS_EVENT*pevent);
ØINT8UOSMboxPost(OS_EVENT*pevent,void*msg);
ØINT8UOSMboxPostOpt(OS_EVENT*pevent,void*msg,INT8Uopt);
ØOS_EVENT*OSMboxDel(OS_EVENT*pevent,INT8Uopt,INT8U*err);
ØINT8UOSMboxQuery(OS_EVENT*pevent,OS_MBOX_DATA*);
2.消息队列msgQ
(1)msgQ基本内容
msgQ是uC/OS-II任务间通信的机制,可实现多条消息传递,即可以同时存储多条消息。
uC/OS-II使用循环队列管理机制。
主要包含在C源文件OS_Q.C中。
msgQ管理:
使用指针数组存储所有消息的位置;使用QCB标识指针数组中消息的基本信息;使用ECB管理整个msgQ。
QCB在编译时分配空间,即当前系统中可用的msgQ个数是预先设置的,系统运行时不能修改。
(2)msgQ全局变量
ØOS_EXTOS_Q*OSQTbl[OS_MAX_QS];//QCB结构体数组
ØOS_EXTOS_Q*OSQFreeList;//空闲QCB头指针
Øtypedefstructos_q{//消息队列控制块
structos_q*OSQPtr;//用于构建空闲QCB链表
void**OSQStart;//指向msgQ指针数组的起始位置
void**OSQEnd;//指向msgQ指针数组的结束位置
void**OSQIn;//指向msgQ指针数组下一个可以插入消息的位置
void**OSQOut;//指向msgQ指针数组下一个可以读出消息的位置
INT16UOSQSize;//msgQ指针数组的大小
INT16UOSQEntries;//msgQ指针数组当前可以读取的消息个数
}OS_Q;
(3)msgQ管理函数
ØOS_EVENT*OSQCreate(void**start,INT16Usize);
ØINT8UOSQPost(OS_EVENT*pevent,void*msg);//发送消息到队尾
ØINT8UOSQPostFront(OS_EVENT*pevent,void*msg);//msg至队首
ØINT8UOSQPostOpt(OS_EVENT*pevent,void*msg,INT8Uopt);
Øvoid*OSQPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
Øvoid*OSQAccept(OS_EVENT*pevent,INT8U*err);
ØOS_EVENT*OSQDel(OS_EVENT*pevent,INT8Uopt,INT8U*err);
ØINT8UOSQQuery(OS_EVENT*pevent,OS_Q_DATA*);
ØINT8UOSQFlush(OS_EVENT*pevent);
(4)msgQ几个问题
uC/OS-II中,什么是事件?
事件是uC/OS-II管理任务间同步与通信的机制。
事件是处理事件的对象感兴趣的,能够感知或捕获到一种事件状态的改变。
3.信号量Sem
Sem主要用来实现任务间同步及标识某类资源的可用个数,即某个特定资源可供多少任务同时使用。
主要包含在C源文件OS_SEM.C中。
ØOS_EVENT*OSSemCreate(INT16Ucnt);
ØvoidOSSemPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
ØINT16UOSSemAccept(OS_EVENT*pevent);
ØINT8UOSSemPost(OS_EVENT*pevent);
ØOS_EVENT*OSSemDel(OS_EVENT*pevent,INT8Uopt,INT8U*err);
ØINT8UOSSemQuery(OS_EVENT*pevent,OS_SEM_DATA*);
ØvoidOSSemSet(OS_EVENT*pevent,INT16Ucnt,INT8U*err);
4.互斥锁Mutex
(1)Mutex基本原理
Mutex用来实现对资源的排他性访问,可能引起优先级反转。
任何任务在占有某个互斥锁事件时,都不能阻塞等待其它任何事件,否则会造成死锁。
主要包含在C源文件OS_MUTEX.C中。
优先级反转是指,低优先级任务占有高优先级任务运行所需的资源,而使高优先级不得不等低优先级任务把资源释放才能执行。
uC/OS-II使用ECB管理Mutex,其成员变量OSEventCnt:
高8位存储Mutex被使用时提供给任务的prio;低8位在没有任务占有Mutex时为0xFF,否则为占有任务的prio。
(2)提升/恢复优先级
a)提升Mutex拥有者任务的优先级的相关操作:
Ø如果该任务原来处于就绪状态,则从就绪表中将其删除;如果该任务正在等待某个事件,则从该事件的任务等待表中将其删除;
Ø修改拥有Mutex的TCB,将其OSTCBPrio修改为欲提升的优先级;
Ø如果该任务处于就绪状态,则将提升的优先级加载到任务就绪表中;如果该任务未就绪且正在等待某个事件,则将提升的优先级添加到该事件的任务等待表中,并修改TCB中OSTCBEventPtr;
Ø修改TCB中与优先级相关的成员变量。
b)恢复Mutex拥有任务的优先级的相关操作:
Ø从任务就绪表中删除提升过的优先级值,修改当前TCB中与优先级相关的所有成员变量;
Ø再次保留提升的优先级值控制块入口,避免将其分配给其它任务。
(3)Mutex管理函数
Ø#defineOS_MUTEX_KEEP_LOWER_80x00FF
Ø#defineOS_MUTEX_KEEP_UPPER_80xFF00
Ø#defineOS_MUTEX_AVAILABLE0x00FF
ØOS_EVENT*OSMutexCreate(INT8Uprio,INT8U*err);
ØvoidOSMutexPend(OS_EVENT*pevent,INT16Utimeout,INT8U*err);
ØINT8UOSMutexAccept(OS_EVENT*pevent,INT8U*err);
ØINT8UOSMutexPost(OS_EVENT*pevent);
ØOS_