第3章 内核结构.docx

上传人:b****8 文档编号:10340894 上传时间:2023-02-10 格式:DOCX 页数:27 大小:345.83KB
下载 相关 举报
第3章 内核结构.docx_第1页
第1页 / 共27页
第3章 内核结构.docx_第2页
第2页 / 共27页
第3章 内核结构.docx_第3页
第3页 / 共27页
第3章 内核结构.docx_第4页
第4页 / 共27页
第3章 内核结构.docx_第5页
第5页 / 共27页
点击查看更多>>
下载资源
资源描述

第3章 内核结构.docx

《第3章 内核结构.docx》由会员分享,可在线阅读,更多相关《第3章 内核结构.docx(27页珍藏版)》请在冰豆网上搜索。

第3章 内核结构.docx

第3章内核结构

第3章内核结构1

3.0临界段(CriticalSections)1

3.1任务1

3.2任务状态3

3.3任务控制块(TaskControlBlocks,OS_TCBs)4

3.4就绪表(ReadyList)7

3.5任务调度(TaskScheduling)10

3.6给调度器上锁和开锁(LockingandUnLockingtheScheduler)11

3.7空闲任务(IdleTask)12

3.8统计任务13

3.9μC/OS中的中断处理16

3.10时钟节拍20

3.11μC/OS-Ⅱ初始化23

3.12μC/OS-Ⅱ的启动24

3.13获取当前μC/OS-Ⅱ的版本号27

第3章内核结构

本章给出μC/OS-Ⅱ的主要结构概貌。

读者将学习以下一些内容;

●μC/OS-Ⅱ是怎样处理临界段代码的;

●什么是任务,怎样把用户的任务交给μC/OS-Ⅱ;

●任务是怎样调度的;

●μC/OS-Ⅱ是怎样知道应用程序CPU的利用率的;

●怎样写中断服务子程序;

●什么是时钟节拍,μC/OS-Ⅱ是怎样处理时钟节拍的;

●μC/OS-Ⅱ是怎样初始化的;

●怎样启动多任务;

本章还描述以下函数,这些服务于应用程序:

●OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL(),

●OSInit(),

●OSStart(),

●OSIntEnter()和OSIntExit(),

●OSSchedLock()和OSSchedUnlock(),以及

●OSVersion().

3.1临界段(CriticalSections)

μC/OS-Ⅱ定义两个宏(macros)来关中断和开中断。

分别是:

OS_ENTER_CRITICAL()和OS_EXIT_CRITICAL()。

在文件OS_CPU.H中。

每种微处理器都有自己的OS_CPU.H文件。

3.2任务

一个任务通常是一个无限的循环[L3.1

(2)],如程序清单3.1所示。

 

程序清单L3.1任务是一个无限循环

voidYourTask(void*pdata)

(1)

{

for(;;){

(2)

/*用户代码*/

调用uC/OS-II的某种系统服务:

OSMboxPend();

OSQPend();

OSSemPend();

OSTaskDel(OS_PRIO_SELF);

OSTaskSuspend(OS_PRIO_SELF);

OSTimeDly();

OSTimeDlyHMSM();

/*用户代码*/

}

}

当任务完成以后,任务可以自我删除,如清单L3.2所示。

注意任务代码并非真的删除了,μC/OS-Ⅱ只是简单地不再理会这个任务了,这个任务的代码也不会再运行。

 

程序清单L3.2.任务完成后自我删除

voidYourTask(void*pdata)

{

/*用户代码*/

OSTaskDel(OS_PRIO_SELF);

}

3.3任务状态

睡眠态(DORMANT):

指任务驻留在程序空间之中,还没有交给μC/OS-Ⅱ管理。

就绪态:

当任务一旦建立,这个任务就进入就绪态准备运行。

运行态:

调用OSStart()可以启动多任务。

等待状态

中断状态

图3.1任务的状态

3.4任务控制块(TaskControlBlocks,OS_TCBs)

一旦任务建立了,任务控制块OS_TCBs将被赋值(程序清单3.3)。

任务建立的时候,OS_TCBs就被初始化。

程序清单L3.3µC/OS-II任务控制块.

typedefstructos_tcb{

OS_STK*OSTCBStkPtr;

#ifOS_TASK_CREATE_EXT_EN

void*OSTCBExtPtr;

OS_STK*OSTCBStkBottom;

INT32UOSTCBStkSize;

INT16UOSTCBOpt;

INT16UOSTCBId;

#endif

structos_tcb*OSTCBNext;

structos_tcb*OSTCBPrev;

#if(OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN||OS_SEM_EN

OS_EVENT*OSTCBEventPtr;

#endif

#if(OS_Q_EN&&(OS_MAX_QS>=2))||OS_MBOX_EN

void*OSTCBMsg;

#endif

INT16UOSTCBDly;

INT8UOSTCBStat;

INT8UOSTCBPrio;

INT8UOSTCBX;

INT8UOSTCBY;

INT8UOSTCBBitX;

INT8UOSTCBBitY;

#ifOS_TASK_DEL_EN

BOOLEANOSTCBDelReq;

#endif

}OS_TCB;

.OSTCBX,.OSTCBY,.OSTCBBitX和.OSTCBBitY用于加速任务进入就绪态的过程或进入等待事件发生状态的过程(避免在运行中去计算这些值)。

这些值是在任务建立时算好的,或者是在改变任务优先级时算出的。

这些值的算法见程序清单L3.4。

程序清单L3.4任务控制块OS_TCB中几个成员的算法

OSTCBY

=priority>>3;

OSTCBBitY

=OSMapTbl[priority>>3];

OSTCBX

=priority&0x07;

OSTCBBitX

=OSMapTbl[priority&0x07];

 

应用程序中可以有的最多任务数(OS_MAX_TASKS)是在文件OS_CFG.H中定义的。

这个最多任务数也是μC/OS-Ⅱ分配给用户程序的最多任务控制块OS_TCBs的数目。

在μC/OS-Ⅱ初始化的时候,如图3.2所示,所有任务控制块OS_TCBs被链接成单向空任务链表。

当任务一旦建立,空任务控制块指针OSTCBFreeList指向的任务控制块便赋给了该任务,然后OSTCBFreeList的值调整为指向下链表中下一个空的任务控制块。

一旦任务被删除,任务控制块就还给空任务链表。

图3.2空任务列表

3.5就绪表(ReadyList)

每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRdyGrp和OSRdyTbl[]。

OSRdyGrp和OSRdyTbl[]之间的关系见图3.3,是按以下规则给出的:

当OSRdyTbl[0]中的任何一位是1时,OSRdyGrp的第0位置1,

当OSRdyTbl[1]中的任何一位是1时,OSRdyGrp的第1位置1,

当OSRdyTbl[2]中的任何一位是1时,OSRdyGrp的第2位置1,

当OSRdyTbl[3]中的任何一位是1时,OSRdyGrp的第3位置1,

当OSRdyTbl[4]中的任何一位是1时,OSRdyGrp的第4位置1,

当OSRdyTbl[5]中的任何一位是1时,OSRdyGrp的第5位置1,

当OSRdyTbl[6]中的任何一位是1时,OSRdyGrp的第6位置1,

当OSRdyTbl[7]中的任何一位是1时,OSRdyGrp的第7位置1,

 

图3.3μC/OS-Ⅱ就绪表

程序清单3.5中的代码用于将任务放入就绪表。

Prio是任务的优先级。

程序清单L3.5使任务进入就绪态

OSRdyGrp|=OSMapTbl[prio>>3];

OSRdyTbl[prio>>3]|=OSMapTbl[prio&0x07];

 

表T3.1OSMapTbl[]的值

Index

BitMask(Binary)

0

00000001

1

00000010

2

00000100

3

00001000

4

00010000

5

00100000

6

01000000

7

10000000

 

如果一个任务被删除了,则用程序清单3.6中的代码做求反处理。

程序清单L3.6从就绪表中删除一个任务

if((OSRdyTbl[prio>>3]&=~OSMapTbl[prio&0x07])==0)

OSRdyGrp&=~OSMapTbl[prio>>3];

为了找到那个进入就绪态的优先级最高的任务,并不需要从OSRdyTbl[0]开始扫描整个就绪任务表,只需要查另外一张表,即优先级判定表OSUnMapTbl[256]。

INT8UconstOSUnMapTbl[]={

0,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x00to0x0F*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x10to0x1F*/

5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x20to0x2F*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x30to0x3F*/

6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x40to0x4F*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x50to0x5F*/

5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x60to0x6F*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x70to0x7F*/

7,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x80to0x8F*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0x90to0x9F*/

5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0xA0to0xAF*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0xB0to0xBF*/

6,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0xC0to0xCF*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0xD0to0xDF*/

5,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0,/*0xE0to0xEF*/

4,0,1,0,2,0,1,0,3,0,1,0,2,0,1,0/*0xF0to0xFF*/

};

 

程序清单L3.7找出进入就绪态的优先级最高的任务

y=OSUnMapTbl[OSRdyGrp];

x=OSUnMapTbl[OSRdyTbl[y]];

prio=(y<<3)+x;

例如:

如果OSRdyGrp的值为二进制01101000,查OSUnMapTbl[OSRdyGrp]得到的值是3;

如果OSRdyTbl[3]的值是二进制11100100,则OSUnMapTbl[OSRdyTbl[3]]的值是2;

优先级Prio就等于26(3*8+2)。

 

3.6任务调度(TaskScheduling)

μC/OS-Ⅱ总是运行进入就绪态任务中优先级最高的那一个。

确定哪个任务优先级最高,下面该哪个任务运行了的工作是由调度器(Scheduler)完成的。

任务级的调度是由函数OSSched()完成的。

中断级的调度是由另一个函数OSIntExt()完成的,这个函数将在以后描述。

OSSched()的代码如程序清单L3.8所示。

程序清单L3.8任务调度器(theTaskScheduler)

voidOSSched(void)

{

INT8Uy;

OS_ENTER_CRITICAL();

if((OSLockNesting|OSIntNesting)==0){

(1)

y=OSUnMapTbl[OSRdyGrp];

(2)

OSPrioHighRdy=(INT8U)((y<<3)+OSUnMapTbl[OSRdyTbl[y]]);

(2)

if(OSPrioHighRdy!

=OSPrioCur){(3)

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];(4)

OSCtxSwCtr++;(5)

OS_TASK_SW();(6)

}

}

OS_EXIT_CRITICAL();

}

任务切换由以下两步完成:

(1)将被挂起任务的微处理器寄存器推入堆栈。

(2)然后将较高优先级的任务的寄存器值从栈中恢复到寄存器中。

3.7给调度器上锁和开锁(LockingandUnLockingtheScheduler)

用于禁止任务调度。

程序清单L3.9给调度器上锁

voidOSSchedLock(void)

{

if(OSRunning==TRUE){

OS_ENTER_CRITICAL();

OSLockNesting++;

OS_EXIT_CRITICAL();

}

}

 

程序清单L3.10给调度器开锁.

voidOSSchedUnlock(void)

{

if(OSRunning==TRUE){

OS_ENTER_CRITICAL();

if(OSLockNesting>0){

OSLockNesting--;

if((OSLockNesting|OSIntNesting)==0){

(1)

OS_EXIT_CRITICAL();

OSSched();

(2)

}else{

OS_EXIT_CRITICAL();

}

}else{

OS_EXIT_CRITICAL();

}

}

}

 

3.8空闲任务(IdleTask)

μC/OS-Ⅱ总是建立一个空闲任务,在没有其它任务进入就绪态时投入运行。

空闲任务设为最低优先级,即OS_LOWEST_PRI0。

程序清单L3.11μC/OS-Ⅱ的空闲任务.

voidOSTaskIdle(void*pdata)

{

pdata=pdata;

for(;;){

OS_ENTER_CRITICAL();

OSIdleCtr++;

OS_EXIT_CRITICAL();

}

}

3.9统计任务

提供运行时间统计的任务叫做OSTaskStat()。

如果用户将系统定义常数OS_TASK_STAT_EN(见文件OS_CFG.H)设为1,这个任务就会建立。

一旦得到了允许,OSTaskStat()每秒钟运行一次(见文件OS_CORE.C),计算当前的CPU利用率。

并放在有符号8位整数OSCPUsage中。

程序清单L3.12初始化统计任务.

voidmain(void)

{

OSInit();/*初始化uC/OS-II

(1)*/

/*安装uC/OS-II的任务切换向量*/

/*创建用户起始任务(为了方便讨论,这里以TaskStart()作为起始任务)

(2)*/

OSStart();/*开始多任务调度(3)*/

}

voidTaskStart(void*pdata)

{

/*安装并启动uC/OS-II的时钟节拍(4)*/

OSStatInit();/*初始化统计任务(5)*/

/*创建用户应用程序任务*/

for(;;){

/*这里是TaskStart()的代码!

*/

}

}

图F3.4统计任务的初始化

 

程序清单L3.13统计任务的初始化.

voidOSStatInit(void)

{

OSTimeDly

(2);

OS_ENTER_CRITICAL();

OSIdleCtr=0L;

OS_EXIT_CRITICAL();

OSTimeDly(OS_TICKS_PER_SEC);

OS_ENTER_CRITICAL();

OSIdleCtrMax=OSIdleCtr;

OSStatRdy=TRUE;

OS_EXIT_CRITICAL();

}

程序清单L3.14统计任务

voidOSTaskStat(void*pdata)

{

INT32Urun;

INT8Susage;

pdata=pdata;

while(OSStatRdy==FALSE){

(1)

OSTimeDly(2*OS_TICKS_PER_SEC);

}

for(;;){

OS_ENTER_CRITICAL();

OSIdleCtrRun=OSIdleCtr;

run=OSIdleCtr;

OSIdleCtr=0L;

OS_EXIT_CRITICAL();

if(OSIdleCtrMax>0L){

usage=(INT8S)(100L-100L*run/OSIdleCtrMax);

(2)

if(usage>100){

OSCPUUsage=100;

}elseif(usage<0){

OSCPUUsage=0;

}else{

OSCPUUsage=usage;

}

}else{

OSCPUUsage=0;

}

OSTaskStatHook();(3)

OSTimeDly(OS_TICKS_PER_SEC);

}

}

3.10μC/OS中的中断处理

μC/OS中,中断服务子程序要用汇编语言来写。

程序清单L3.15μC/OS-II中的中断服务子程序.

用户中断服务子程序:

保存全部CPU寄存器;

(1)

调用OSIntEnter或OSIntNesting直接加1;

(2)

执行用户代码做中断服务;(3)

调用OSIntExit();(4)

恢复所有CPU寄存器;(5)

执行中断返回指令;(6)

图3.5中断服务

进入中断函数OSIntEnter()的代码如程序清单L3.16所示,

程序清单L3.16通知μC/OS-Ⅱ,中断服务子程序开始了.

voidOSIntEnter(void)

{

OS_ENTER_CRITICAL();

OSIntNesting++;

OS_EXIT_CRITICAL();

}

 

程序清单L3.17通知μC/OS-Ⅱ,脱离了中断服务

voidOSIntExit(void)

{

OS_ENTER_CRITICAL();

(1)

if((--OSIntNesting|OSLockNesting)==0){

(2)

OSIntExitY=OSUnMapTbl[OSRdyGrp];(3)

OSPrioHighRdy=(INT8U)((OSIntExitY<<3)+

OSUnMapTbl[OSRdyTbl[OSIntExitY]]);

if(OSPrioHighRdy!

=OSPrioCur){

OSTCBHighRdy=OSTCBPrioTbl[OSPrioHighRdy];

OSCtxSwCtr++;

OSIntCtxSw();(4)

}

}

OS_EXIT_CRITICAL();

}

图3.6中断中的任务切换函数OSIntCtxSw()调整栈结构

 

3.11时钟节拍

μC/OS需要用户提供周期性信号源,用于实现时间延时和确认超时。

节拍率应在每秒10次到100次之间。

用户必须在多任务系统启动以后再开启时钟节拍器,也就是在调用OSStart()之后。

程序清单L3.19启动时钟就节拍器的不正确做法.

voidmain(void)

{

.

.

OSInit();/*初始化uC/OS-II*/

/*应用程序初始化代码...*/

/*...通过调用OSTaskCreate()创建至少一个任务*/

.

允许时钟节拍(TICKER)中断;/*千万不要在这里允许时钟节拍中断!

!

!

*/

.

OSStart();/*开始多任务调度*/

}

 

μC/OS-Ⅱ中的时钟节拍服务是通过在中断服务子程序中调用OSTimeTick()实现的。

 

程序清单L3.20时钟节拍中断服务子程序的示意代码

voidOSTickISR(void)

{

保存处理器寄存器的值;

调用OSIntEnter()或是将OSIntNesting加1;

调用OSTimeTick();

调用OSIntExit();

恢复处理器寄存器的值;

执行中断返回指令;

}

 

程序清单L3.21时钟节拍函数OSTimtick()的一个节拍服务

voidOSTimeTick(void)

{

OS_TCB*ptcb;

OSTimeTickHook();

(1)

ptcb=OSTCBList;

(2)

while(ptcb->OSTCBPrio!

=OS_IDLE_PRIO){(3)

OS_ENTER_CRITICAL();

if(ptcb->OSTCBDly!

=0){

if(--ptcb->OSTCBDly==0){

if(!

(ptcb->OSTCBStat&OS_STAT_SUSPEND)){(4)

OSRdyGrp|=ptcb->OSTCBBitY;(5)

OSRdyTbl[ptcb->OSTCBY]|=ptcb->OSTCBBitX;

}else{

ptcb->OSTCBDly=1;

}

}

}

ptcb=ptcb->OSTCBNext;

OS_EXIT_CRITICAL

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 临床医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1