uc/os中OSTaskCreate()函数分析

原创 2011年01月20日 17:07:00

OSTaskCreate()函数分析

uc/os系统中,对于任务的描述和管理是通过任务控制快-OS_TCB来实现的,OS_TCB本质上是一些驻留在在RAM中的结构体。由以下内容构成

typedef struct os_tcb {

    OS_STK        *OSTCBStkPtr;//OS_STK定义为32位无符号数据,该行定义当前任务所分配的堆栈的栈顶指针(该栈顶指针是指任务切换后自动保存的r0~r15等一系列数据后的堆栈指针),对于堆栈,uc/os可以对每一个任务分配一个任意大小的堆栈。

 

#if OS_TASK_CREATE_EXT_EN//如果使能勒扩展任务控制块,则定义以下数据

    void          *OSTCBExtPtr;//扩展任务块指针,扩展控制块的引入,使得可以在不改变uc/os源代码的前提下,加入扩展功能,此外如果需要使用该功能,则需在OS_CFG.Huc/os配置文件)中将OS_TASK_CREATE_EXT_EN置一,允许建立任务函数的扩展

    OS_STK        *OSTCBStkBottom;指向任务堆栈的栈底(就是数据最后进入的地址)如果堆栈增长方式是递增的话,那么它指向堆栈的最高地址,反之指向最低地址,改参数在使用OSTaskStkChk()函数是需要使用

    INT32U         OSTCBStkSize;//该参数是任务堆栈大小,对于堆栈大小是指栈中所能容纳的指针数目,而不是字节数目。假设堆栈容量是1000,如果地址宽度是32位的,那么堆栈包含4000字节,但是其容量是1000

    INT16U         OSTCBOpt;//选择项,支持三个选择,OS_TASK_OPT_STK_CHK该参数用于告知TaskCreateExt()函数在建立任务时对堆栈进行检查(uc/os不会自动进行堆栈检查,必须使用改选项来设定),OS_TASK_OPT_STK_CLR该参数设定,则在任务建立的过程中将任务栈清零(只有在需要使用栈检验功能时才将栈清零)OS_TASK_OPT_SAVE_FP该参数是通知任务需要做浮点数运算

    INT16U         OSTCBId;用于存储任务的识别码,现在还没使用,感觉他会发展风linux中的PID

#endif

 

    struct os_tcb *OSTCBNext;

    struct os_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

 

    INT16U         OSTCBDly;//任务延时的时间,或者等待某事件发生的超时限制,在时钟没每生一次中断的时候,时钟节拍函数OSTimeTick()将通过任务控制块的链表访问该变量并将其减一从而刷新该变量

    INT8U          OSTCBStat;//该变量用于描述任务的状态

    INT8U          OSTCBPrio;//任务优先级

 

      INT8U          OSTCBX;//表示任务优先级在就绪表中的X轴的位置,相当说明该优先级是某一组中的哪一个位元素。

    INT8U          OSTCBY;//表示任务优先级在就绪表中的Y轴的位置,相当说明该优先级是处于第几个组元素

    INT8U          OSTCBBitX;//说明该优先级对应的OSTCBTBL[prio&0x7]中元素的赋值

    INT8U          OSTCBBitY;//说明该优先级对应的OSRdyGrp的元素,将该元素与OSRdyGrp相于就获得当前的OSRdyGrp所对应的值

 

#if OS_TASK_DEL_EN

    BOOLEAN        OSTCBDelReq;该参数表明该任务是否需要删除。只有在OS_FLAG_EN置为一是才会出现在OS_TCB

#endif

} OS_TCB;

Ø  对于OS_TCB的管理,uc/os采用了两个链表进行管理,在任务初始化时所有的空闲OS_TCB被连接成单向的空任务链表。另外当任务建立时,空任务控制块指针OSTCBFreeList指向的控制块分配给该任务,当有多个任务建立时,所申请的任务控制块构成一个双向的任务控制块链表

Ø  对于任务就绪表的说明:在uc/os中最多支持64个任务,并为每一个任务指派一个优先级,对于任务的就绪查询,其使用任务就绪表。每个任务的就绪态标志都放入就绪表中的,就绪表中有两个变量OSRedyGrpOSRdyTbl[]。在OSRdyGrp中,任务按优先级分组,8个任务为一组。OSRdyGrp中的每一位表示8组任务中每一组中是否有进入就绪态的任务。任务进入就绪态时,就绪表OSRdyTbl[]中的相应元素的相应位也置位。就绪表OSRdyTbl[]数组的大小取决于OS_LOWEST_PR1O(见文件OS_CFG.H)。当用户的应用程序中任务数目比较少时,减少OS_LOWEST_PR1O的值可以降低μC/OS-Ⅱ对RAM(数据空间)的需求量。

Ø  任务堆栈的栈顶是指,将CPU全部寄存器压入堆栈后的SP,而不是说堆栈的起始地址。

 

 

 

 

 

 

 

OS_TCBInit()函数分析

函数名称:

OS_TCBInit()

函数功能:

任务控制块初始化

函数入口接口:

任务优先级prio、任务堆栈栈顶ptos、任务堆栈栈底pbos、任务id、任务堆栈大小stk_size、任务扩展指针pext、任务选项opt

函数出口接口:

8位无符号数,表明是否初始化成功

主要调用函数:

函数主要执行逻辑流程:

 

OS_TCBInit()源码

INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)
{
#if OS_CRITICAL_METHOD == 3         //
    OS_CPU_SR cpu_sr;
#endif   
    OS_TCB    *ptcb;//
声明一个指向任务控制块的指针


    OS_ENTER_CRITICAL();//
防止其他任务抢占该TCB故要关中断
    ptcb = OSTCBFreeList;//
从空任务链表中申请一个新的控制块(空任务链表是单向链表,大小由OS_MASK_TASKS决定)                                 

if (ptcb != (OS_TCB *)0) {//如果指针分配成功,即分配的tcb有实际地址
        OSTCBFreeList        =ptcb->OSTCBNext;//
则空任务块指针OSTCBFreeList指向下一个空TCB

OS_EXIT_CRITICAL();//此时该任务已经占有该任务控制块,开中断来初始化TCB中其余变元,减少关中断时间。
        ptcb->OSTCBStkPtr    =ptos; //
初始话任务堆栈栈顶(参数传入)

    ptcb->OSTCBPrio      = (INT8U)prio;//初始化优先级(参数传入)
        ptcb->OSTCBStat      = OS_STAT_RDY;//
任务状态为就绪

    ptcb->OSTCBDly       = 0;//设置任务延时为0,即不延时

#if OS_TASK_CREATE_EXT_EN > 0//如果在OS_FH.H中使能了OS_TASK_CREATE_EXT_EN额外的变元就被插入到TCB
        ptcb->OSTCBExtPtr    = pext
//任务扩展块的指针
        ptcb->OSTCBStkSize   = stk_size
//任务对战大小

           ptcb->OSTCBStkBottom = pbos;//堆栈的栈底指针                      
        ptcb->OSTCBOpt       = opt;//
任务选项
        ptcb->OSTCBId        = id;//
任务ID(无实际作用)
#else
        pext                 = pext;//
防止编译器报警                      
        stk_size             = stk_size;
        pbos                 = pbos;
        opt                  = opt;
        id                   = id;
#endif

#if OS_TASK_DEL_EN > 0
        ptcb->OSTCBDelReq    = OS_NO_ERR;//
布尔变量是否出现取决于OS_TASK_EN是否被设置(如果不打算删除任务的话,将节省一个布尔变量的空间)
#endif

        ptcb->OSTCBY         = prio >> 3;//提前计算任务就绪表所需要的一些变量
        ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];
        ptcb->OSTCBX         = prio & 0x07;
        ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];

#if OS_EVENT_EN > 0
        ptcb->OSTCBEventPtr = (OS_EVENT *)0;//
如果使用信号量,邮箱,队列则该指针指向事件控制块
#endif

#if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) && (OS_TASK_DEL_EN > 0)//如果事件标志得到使用
        ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0;//
初始化指向事件标志节点的指针
#endif

#if (OS_MBOX_EN > 0) || ((OS_Q_EN > 0) && (OS_MAX_QS > 0))
        ptcb->OSTCBMsg       = (void *)0;//
初始化指向邮箱、队列、信号量集所在消息的指针
#endif

#if OS_VERSION >= 204
        OSTCBInitHook(ptcb);//
钩子函数的初始化(具体内容由自己实现)
#endif

        OSTaskCreateHook(ptcb);//调用钩子函数       
        OS_ENTER_CRITICAL();//
关中断
        OSTCBPrioTbl[prio] = ptcb;//
将任务控制块优先级表中对应变量赋值为该TCB地址
        ptcb->OSTCBNext    = OSTCBList;//
将初始化好的TCB加入到以用TCB双向链表中
        ptcb->OSTCBPrev    = (OS_TCB *)0;//TCB
链表的每一次加入,总是加入到链表最前面的一个位置,故新加入的TCB前一个TCB应该为空
        if (OSTCBList != (OS_TCB *)0) {//OSTCBList
是双向链表的头结点,如果头结点是非空的话,证明该链表中非空了,故需将新申请的TCB块于前一个TCB连接起来
            OSTCBList->OSTCBPrev = ptcb;//
将前一个TCB控制块中的OSTCBPrev指向新申请的TCB,此时OSTCBList指向的是旧的TCB
        }
        OSTCBList               = ptcb;//
TCB加入成功后,将双向链表头指针指向新加入的TCB(新的TCB总是加载到双向链表的链表头)
        OSRdyGrp               |= ptcb->OSTCBBitY//
更新任务就绪表的内容(此次修改任务就绪组变量其中OSTCBBitY已经在前面计算过)
        OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;//
更新任务就绪位表
        OS_EXIT_CRITICAL();
        return (OS_NO_ERR);
返回TCB初始化成功
    }
    OS_EXIT_CRITICAL();
    return (OS_NO_MORE_TCB);//
如果该TCB指针为零则返回OS_NO_MORE_TCB
}

Ø  该函数的基本过程:申请TCB->初始化TCB中的变量->更新任务控制块优先级表->TCB加入到以用TCB链表->更新任务就绪表

 

 

 

OS_TaskStkInit()分析

函数名称:

OS_TaskStkInit()

函数功能:

任务堆栈初始化

函数入口接口:

任务入口地址、任务参数、堆栈原始栈顶、选项

函数出口接口:

初始化寄存器后的堆栈地址(堆栈栈顶)

主要调用函数:

逻辑流程

示意性代码:

OS_STK *OSTaskStkInit(void (*task)(void *pd),void *pdata,OS_STK *ptos,INT16U opt)

{

 模拟带参数(pdata)的函数调用

模拟ISR向量

按照预先设计的寄存器值初始化堆栈结构

返回栈顶指针给调用该函数的函数

 

 

 

 

 

 

 

 

OSTaskStkInit()函数源码(针对ARM7LPC2200,该源码根据不同的CPU有不同的形式)

OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt)

{

    OS_STK *stk;

    opt    = opt;                           /* 'opt'  没有使用。作用是避免编译器警告    */

stk    = ptos;                          /* 获取堆栈指针                                       */

/* 建立任务环境,使用满递减堆栈       */

    *stk = (OS_STK) task;                   /*  pc  */

    *--stk = (OS_STK) task;                 /*  lr  */

 

    *--stk = 0;                             /*  r12  */

    *--stk = 0;                             /*  r11  */

    *--stk = 0;                             /*  r10  */

    *--stk = 0;                             /*  r9   */

    *--stk = 0;                             /*  r8   */

    *--stk = 0;                             /*  r7   */

    *--stk = 0;                             /*  r6   */

    *--stk = 0;                             /*  r5   */

    *--stk = 0;                             /*  r4   */

    *--stk = 0;                             /*  r3   */

    *--stk = 0;                             /*  r2   */

    *--stk = 0;                             /*  r1   */

    *--stk = (unsigned int) pdata;          /*  r0,第一个参数使用R0传递   */

    *--stk = (USER_USING_MODE|0x00);     /*  spsr,允许 IRQ, FIQ 中断   */

    *--stk = 0;                             /*  关中断计数器OsEnterSum;    */

 

    return (stk);

}

Ø  在任务第一次开始执行时,操作系统首先得到任务的任务控制块,然后从任务控制块得到任务的堆栈指针,再把这个堆栈指针送到R13(SP)。然后再OSCtxSw()函数中把初始化的各个寄存器的值送到对应的CPU寄存器,所以在OSTaskStkInit()函数里不用处理R13。在任务执行过一次之后,任务被中断切换到其它任务之前,就会从R13(SP)得到堆栈指针当前的位置,在OSCtxSw()函数中把CPUR0-R12,R14等值压到R13(SP)指定的堆栈中。所以R13的内容是不需要初始化的。

Ø  OSTaskStkInit()中数据进栈的顺序要和OSCtxSw()中数据出栈的顺序对应。
PC
值是最后出栈,所以要最先进栈。把任务函数的地址压入堆栈。出栈后,任务函数地址送入PC后,就开始执行任务函数。

Ø  R14是返回地址,但是任务函数是一个无限循环,只有在任务调度时才会退出,而在任务调度时切换到另一个任务时,会把另一个任务堆栈中存储的R14的值送到R14,保证程序在另一个任务被中断时的断点继续运行。所以在初始化堆栈时,R14的值是没有用的,可以随意赋值。

Ø  R1-R12可随便赋值,但是赋值的形式最好是反映出堆栈增长方式

Ø  返回的地址是堆栈压入上述寄存器后的指针,而不是对战最初的指针

Ø  CPSR在压入堆栈的时候确保其是允许IRQFIQ

 

 

 

 

OS_Sched()函数分析

函数名称:

OS_Sched()

函数功能:

任务调度(该函数相当于判断是否需要进行任务切换,具体任务切换是由OS_TASK_SW()汇编宏来完成的

函数入口接口:

函数出口接口:

主要调用函数:

OS_TASK_SW()

函数逻辑流程

OSSched()函数源码

void OSSched (void)

{

    INT8U y;

 

 

    OS_ENTER_CRITICAL();

    if ((OSLockNesting | OSIntNesting) == 0) {//判断任务调度器是否上锁、该任务切换请求是否是在ISR中请求的

        y             = OSUnMapTbl[OSRdyGrp];//获得当前最高优先级任务处于就绪表的第几组

        OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);//OSUNMapTbly】是获得其处于组元素中的第几位,将组元素左移三位相加位元素后强制类型转换得当前优先级最高的任务,这个表是uc/os提前设定好的,其实我也每台看明白他是怎么来的

        if (OSPrioHighRdy != OSPrioCur) {//判断最高优先级任务是否是现在运行的任务

            OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy];//根据当前最高优先级从任务控制块优先级表中获得当前最高优先级控制块的入口地址

            OSCtxSwCtr++;//任务切换次数++,该变量是跟踪任务切换次数的                                                        

            OS_TASK_SW();//宏调用,完成实际上的任务切换

        }

    }

    OS_EXIT_CRITICAL();

}

Ø  在任务切换的所有代码都是临界段代码,这样是防止ISR在将高优先级任务置位,干扰此次任务切换。

Ø  判断任务切换请求是否是ISR的请求的原因是OS_TASK_SW()应用在任务级,它只是简单的将处理器寄存器保存到被挂起的任务堆栈中,并且从堆栈中回复要运行的更高优先级任务。

 

 

OS_TASK_SW宏调用分析

函数名称:

OS_TASK_SW(实际上是调用软中断)

函数功能:

完成任务切换

函数入口接口:

函数出口接口:

主要调用函数:

软中断

 

#define OS_TASK_SW() asm INT #080h //此处使用SWI指令进行分析

 

Ø  对于任务的切换uc/os使用软中断来实现

对于软中断将会在专门来说明一下,这点看了好久才JB看明白

 

 

 

 

 

 

OSTaskCreate()分析

前面说了那么多,终于该到OSTaskCreate()勒,在uc/os中,该函数的作用是创建一个任务(其实就是个线程)来完成一定的功能。

函数名称:

OSTaskCreate()

函数功能:

建立一个任务

函数入口接口:

void (*task)(void *pd)任务入口地址指针

void *pdata传给任务参数的指针(一般用不到)

OS_STK *ptos任务堆栈栈顶指针(在初始化任务堆栈的时候使用)

INT8U prio任务优先级,作用你懂得

函数出口接口:

INT8U 是否建立成功标志

主要调用函数:

OSTaskStkInit()任务堆栈初始化

OSTCBInit*()任务控制快初始化,说白了就是从空TCB取个空TCB然后该结构体内容赋值,再将该TCB加载到双向链表上

OSSched()任务调度

函数流程逻辑

 

 

 

OSTaskCreate()源码分析

INT8U OSTaskCreate (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio)

{

#if OS_CRITICAL_METHOD == 3

    OS_CPU_SR  cpu_sr;

#endif

    OS_STK    *psp;

    INT8U      err;

   

#if OS_ARG_CHK_EN > 0

    if (prio > OS_LOWEST_PRIO) { //确保该优先级高于设定的最低优先级  

        return (OS_PRIO_INVALID);

    }

#endif

    OS_ENTER_CRITICAL();

    if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { //确保该优先级还未分配,如果该优先级已被分配,那么在任务控制块优先级表中存放的是实际分配任务控制块的入口地址,未分配则地址为空

        OSTCBPrioTbl[prio] = (OS_TCB *)1; //只是简单的将该优先级对应数组元素置位一,表示该优先级现在被分配了,其不是赋值具体的TCB入口地址是为了减少关中断的时间,(具体入口地址是在OSTCBINI()函数中从空任务控制池中分配的)

                                             /

        OS_EXIT_CRITICAL();

        psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0);//堆栈初始化,返回的是压入一定寄存器后的SP   

        err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);//初始化TCB

        if (err == OS_NO_ERR) {

            OS_ENTER_CRITICAL();

            OSTaskCtr++; //该变量是用来追踪当前任务数目的

            OS_EXIT_CRITICAL();

            if (OSRunning == TRUE) { //确保uc/os已经开始多任务调度了

                OS_Sched();//找到当前最高优先级任务,开始任务调度

            }

        } else {

            OS_ENTER_CRITICAL();

            OSTCBPrioTbl[prio] = (OS_TCB *)0;//如果TCB申请失败,那么将任务控制块优先级表对应元素清零,放弃此次任务建立

            OS_EXIT_CRITICAL();

 

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

uc/os-iii学习笔记-OSTaskCreate

必须为该任务分配一个任务控制块(OS_TCB)。 static OS_TCB AppTaskStartTCB 每个任务都必须有属于自己的栈(CPU_STK)。 static CPU_ST...

uc/os中OSStart()函数分析

OSStart()函数分析多任务的的启动是通过调用OSStart()实现的,而在启动uc/os之前至少需要建立一个应用任务。OSStart()函数源码void  OSStart (void){    ...

uc/os中OSSched()函数分析

OS_Sched()分析         在uc/os中总是运行优先级最高的就绪任务,确定哪个任务优先级最高,该由哪个优先级人物运行了,这一工作是由任务调度器完成的,(而具体的任务切换,是任务调度器在...

UC/OS的46个系统函数2

INT32U OSTimeGet (void); 所属文件 OS_TIMC.C 调用者 任务或中断 开关量 无 OSTimeGet()获取当前系统时钟数值。系统时钟是一个32位的计数器,记录系统上...

uc/os之延时函数简析

uc/os中OSTimeDly与OSTimeDlyHMSM的区别 在uc/os-II的系统中,规定,除了空闲任务之外的所有任务必须在任务中合适的位置调用系统提供的函数OSTimeDly();使当...

uC/OS-II之系统函数

原文地址:博客园任务管理1 OSTaskCreate()建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立。中断处理程序中不能 建立任务。一个任务可以为无限循环的结构...

uC/OS-II 常用函数参考手册

任务管理 1 OSTaskCreate()     建立一个新任务。任务的建立可以在多任务环境启动之前,也可以在正在运行的任务中建立。中断处理程序中不能建立任务。一个任务可以为无限循环的结...

uC/OS-II 函数之OSInit()

对于有热心的小伙伴在微博上私信我,说我的uC/OS-II 一些函数简介篇幅有些过于长应该分开介绍。应小伙伴的要求,特此将文章分开进行讲解。本文主要介绍OSInit()初始化函数

UC/OS-II基础知识之事件控制块及事件处理函数

UC/OS-II基础知识之事件控制块及事件处理函数1.等待任务列表 作为功能完善的事件,应该对那些处于等待任务具有两方面的管理功能,一是要对等待事件的所有任务进行记录并排序,而是允许等待任务有一个等...

uC/OS-II内存管理函数的二维指针

uC/OS-II内存管理函数内最难理解的部分就是二维指针,本文以图文并茂的方式对二维指针进行了详细分析与讲解。看完本文,相信对C里面指针的概念又会有进一步的认识。   一、OSMemCreate(...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)