OSTaskCreate

OSTaskCreate负责创建Task所需的数据结构,该函数原形如下所示:

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

其中task是一个函数指针,指向该Task所开始的函数,当这个Task第一次被调度运行时将会从task处开始运行。

p_arg是传给task的参数指针;

ptos是堆栈指针,指向栈顶(堆栈从上往下)或栈底(堆栈从下往上);

prio是进程的优先级,uC/OS-II共支持最大64个优先级,其中最低的两个优先级给Idle和Stat进程,并且各个Task的优先级必须不同。

接下来,我们看看这个函数的执行流程:

#if OS_ARG_CHK_EN > 0

if (prio > OS_LOWEST_PRIO) {

return (OS_PRIO_INVALID);

}

#endif

OS_ENTER_CRITICAL();

if (OSIntNesting > 0) {

OS_EXIT_CRITICAL();

return (OS_ERR_TASK_CREATE_ISR);

}

if (OSTCBPrioTbl[prio] == (OS_TCB *)0) {

OSTCBPrioTbl[prio] = (OS_TCB *)1;

OS_EXIT_CRITICAL();

psp = (OS_STK *)OSTaskStkInit(task, p_arg, ptos, 0);

err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0);

if (err == OS_NO_ERR) {

if (OSRunning == TRUE) {

OS_Sched();

}

} else {

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio] = (OS_TCB *)0;

OS_EXIT_CRITICAL();

}

return (err);

}

OS_EXIT_CRITICAL();

return (OS_PRIO_EXIST);

OS_LOWEST_PRIO在ucos-ii.h中被定义为63,表示Task的优先级从0到63,共64级。首先判断prio是否超过最低优先级,如果是,则返回OS_PRIO_INVALID错误。

然后调用OS_ENTER_CRITICAL(),进入临界段,在临界段中的代码执行不允许被中断。这个宏是用户自定义的,一般是进行关中断操作,例如在x86中的CLI等。这个宏和OS_EXIT_CRITICAL()相对应,这个宏表示离开临界段。

OSTaskCreate不允许在中断中调用,因此会判断OSIntNesting是否大于0,如果大于0,表示正在中断嵌套,返回OS_ERR_TASK_CREATE_ISR错误。

接着判断该prio是否已经有Task存在,由于uC/OS-II只支持每一个优先级一个Task,因此如果该prio已经有进程存在,OSTaskCreate会返回OS_PRIO_EXIST错误。

相反,如果该prio先前没有Task存在,则将OSTCBPrioTbl[prio]置1,表示该prio已被占用,然后调用OSTaskStkInit初始化堆栈,调用OS_TCBInit初始化TCB,如果OSRunning为TRUE表示OS正在运行,则调用OS_Sched进行进程调度;否则返回。

下面来看看OSTaskStkInit和OS_TCBInit这两个函数。

OSTaskStkInit是一个用户自定义的函数,因为uC/OS-II在设计时无法知道当前处理器在进行进程调度时需要保存那些信息,OSTaskStkInit就是初始化堆栈,让Task看起来就好像刚刚进入中断并保存好寄存器的值一样,当OS_Sched调度到该Task时,只需切换到该堆栈中,将寄存器值Pop出来,然后执行一个中断返回指令IRET即可。

OSTaskStkInit的原型如下:

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

和OSTaskCreate类似,task是进程入口地址,pdata是参数地址,ptos是堆栈指针,而opt只是作为一个预留的参数Option而保留。返回的是调整以后的堆栈指针。

在OSTaskStkInit中,一般是将pdata入栈,flag入栈,task入栈,然后将各寄存器依次入栈。

OS_TCBInit初始化TCB数据结构,下面只提取主要部分来看:

INT8U  OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt)

{

OS_TCB    *ptcb;

OS_ENTER_CRITICAL();

ptcb = OSTCBFreeList;

if (ptcb != (OS_TCB *)0) {

OSTCBFreeList        = ptcb->OSTCBNext;

OS_EXIT_CRITICAL();

ptcb->OSTCBStkPtr    = ptos;

ptcb->OSTCBPrio      = prio;

ptcb->OSTCBStat      = OS_STAT_RDY;

ptcb->OSTCBPendTO    = FALSE;

ptcb->OSTCBDly       = 0;

#if OS_TASK_CREATE_EXT_EN > 0

ptcb->OSTCBExtPtr    = pext;

ptcb->OSTCBStkSize   = stk_size;

ptcb->OSTCBStkBottom = pbos;

ptcb->OSTCBOpt       = opt;

ptcb->OSTCBId        = 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;

#endif

ptcb->OSTCBY         = (INT8U)(prio >> 3);

ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];

ptcb->OSTCBX         = (INT8U)(prio & 0x07);

ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];

#if OS_EVENT_EN

ptcb->OSTCBEventPtr  = (OS_EVENT *)0;

#endif

OSTaskCreateHook(ptcb);

OS_ENTER_CRITICAL();

OSTCBPrioTbl[prio] = ptcb;

ptcb->OSTCBNext    = OSTCBList;

ptcb->OSTCBPrev    = (OS_TCB *)0;

if (OSTCBList != (OS_TCB *)0) {

OSTCBList->OSTCBPrev = ptcb;

}

OSTCBList               = ptcb;

OSRdyGrp               |= ptcb->OSTCBBitY;

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

OSTaskCtr++;

OS_EXIT_CRITICAL();

return (OS_NO_ERR);

}

OS_EXIT_CRITICAL();

return (OS_NO_MORE_TCB);

}

首先调用OS_ENTER_CRITICAL进入临界段,首先从OSTCBFreeList中拿出一个TCB,如果OSTCBFreeList为空,则返回OS_NO_MORE_TCB错误。

然后调用OS_EXIT_CRITICAL离开临界段,接着对该TCB进行初始化:

将OSTCBStkPtr初始化为该Task当前堆栈指针;

OSTCBPrio设置为该Task的prio;

OSTCBStat设置为OS_STAT_RDY,表示就绪状态;

OSTCBDly设置为0,当该Task调用OSTimeDly时会初始化这个变量为Delay的时钟数,然后Task转入OS_STAT_状态。这个变量在OSTimeTick中检查,如果大于0表示还需要进行Delay,则进行减1;如果等于零表示无须进行Delay,可以马上运行,转入OS_STAT_RDY状态。

OSTCBBitY和OSTCBBitX的作用我们在等会专门来讨论。

紧接着就要将该TCB插入OSTCBList列表中,先调用OS_ENTER_CRITICAL进入临界段,将该TCB插入到OSTCBList成为第一个节点,然后调整OSRdyGrp和OSRdyTbl,(这两个变量一会和OSTCBBitX/OSTCBBitY一起讨论),最后将OSTaskCtr计数器加一,调用OS_EXIT_CRITICAL退出临界段。

OSMapTbl和OSUnMapTbl

刚才我们看到TCB数据结构中的OSTCBBitX/OSTCBBitY以及OSRdyGrp/OSRdyTbl的使用,这里专门来讨论讨论这几个变量的用法。

uC/OS-II将64个优先级的进程分为8组,每组8个。刚好可以使用8个INT8U的数据进行表示,于是这就是OSRdyGrp和OSRdyTbl的由来,OSRdyGrp表示组别,从0到7,从前面我们可以看到OSRdyGrp和OSRdyTbl是这么被赋值的:

OSRdyGrp               |= ptcb->OSTCBBitY;

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

也就是OSTCBBitY保存的是组别,OSTCBBitX保存的是组内的偏移。而这两个变量是这么被初始化的:

ptcb->OSTCBY         = (INT8U)(prio >> 3);

ptcb->OSTCBBitY      = OSMapTbl[ptcb->OSTCBY];

ptcb->OSTCBX         = (INT8U)(prio & 0x07);

ptcb->OSTCBBitX      = OSMapTbl[ptcb->OSTCBX];

由于prio不会大于64,prio为6位值,因此OSTCBY为prio高3位,不会大于8,OSTCBX为prio低3位。

这里就涉及到OSMapTbl数组和OSUnMapTbl数组的用法了。我们先看看OSMapTbl和OSUnMapTbl的定义:

INT8U  const  OSMapTbl[8]   = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};

INT8U  const  OSUnMapTbl[256] = {

0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

7, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

6, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

5, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0,

4, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0

};

OSMapTbl分别是一个INT8U的八个位,而OSUnMap数组中的值就是从0x00到0xFF的八位中,每一个值所对应的最低位的值。我们在调度的时候只需将OSRdyGrp的值代入OSUnMapTbl数组中,得到OSUnMapTbl[OSRdyGrp]的值就是哪个优先级最高的Group有Ready进程存在,再使用该Group对应OSRdyTbl[]数组中的值一样带入OSUnMapTbl中就可以得出哪个Task是优先级最高的。

于是我们提前来看看OS_Sched()中获取最高优先级所使用的方法:

y             = OSUnMapTbl[OSRdyGrp];

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

显然,先得到的y就是存在最高优先级的Group,然后OSUnMapTbl[OSRdyTbl[y]]就是Group中的偏移,因此OSPrioHighRdy最高优先级就应该是Group<<3再加上这个偏移。

于是乎,我们就可以对上面那一小段很模糊的代码做一下总结:

prio只有6位,高3位代表着某一个Group保存在OSTCBY中,OSTCBBitY表示该Group所对应的Bit,将OSRdyGrp的该位置1表示该Group中有进程是Ready的;低3位代表着该Group中的第几个进程,保存在OSTCBX中,OSTCBBitX表示该进程在该Group中所对应的Bit,OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX就等于将该进程所对应的Bit置1了。

OSStart

OK,接下来我们来看这个开始函数了。OSStart其实很短,只有匆匆几句代码:

void  OSStart (void)

{

INT8U y;

INT8U x;

if (OSRunning == FALSE) {

y             = OSUnMapTbl[OSRdyGrp];

x             = OSUnMapTbl[OSRdyTbl[y]];

OSPrioHighRdy = (INT8U)((y << 3) + x);

OSPrioCur     = OSPrioHighRdy;

OSTCBHighRdy  = OSTCBPrioTbl[OSPrioHighRdy];

OSTCBCur      = OSTCBHighRdy;

OSStartHighRdy();

}

}

如果OSRunning为TRUE,表示OS已经在运行了,则OSStart不做任何事。

OSRunning为FALSE,则找出最高优先级的Ready的Task,并将该指针赋给OSTCBHighRdy和OSTCBCur。然后调用OSStartHighRdy()开始运行该进程。

OSStartHighRdy()为用户自定义函数,在这个函数中,主要功能就是进行堆栈切换并将OSRunning设置为TRUE表示OS已经开始运行,然后将保存的寄存器弹出,最后执行中断返回指令IRET就跳到OSTCBHighRdy的最开始处运行了。

uC/OS-II源码分析(总体思路 二)相关推荐

  1. uC/OS-II源码分析(总体思路 三)

    OSTimeDly 在Task中,一般执行一段时间之后调用OSTimeDly推迟一段时间再继续运行,OSTimeDly将本进程从Ready TCBList中删除,然后将Delay的时间设置给OSTCB ...

  2. 【嵌入式系统—实时操作系统】uC/OS II源码的官网下载

    要下载源码为:STM32F107-uC/OS-II源码包 Micrium官网的下载地址:Micrium_uC-Eval-STM32F107_uCOS-II 怎么找? 到官网的产品专区目录入口查找STM ...

  3. uC/OS-II源码分析(总体思路一)(

    首先从main函数开始,下面是uC/OS-II main函数的大致流程: main(){ OSInit(); TaskCreate(...); OSStart(); } 首先是调用OSInit进行初始 ...

  4. PX4 Autopilot源码分析 - 总体架构

    PX4 Autopilot源码分析 - 总体架构 应用场景 单独飞控 飞控+任务计算机场景 软件架构 译自PX4 user guide,原文请参阅: https://docs.px4.io/maste ...

  5. idea 线程内存_Java线程池系列之-Java线程池底层源码分析系列(二)

    课程简介: 课程目标:通过本课程学习,深入理解Java线程池,提升自身技术能力与价值. 适用人群:具有Java多线程基础的人群,希望深入理解线程池底层原理的人群. 课程概述:多线程的异步执行方式,虽然 ...

  6. PDF阅读器系列之--MuPDF源码分析过程(二)

    博客找回来了,在那更新 http://blog.csdn.net/sky_pjf 前 时间好快,又一周过了,发现自己太忙了,博客都没去管-- 序 *MuPDF开源框架现在一直都在维护,我一般都会隔一周 ...

  7. 【转】ABP源码分析四十二:ZERO的身份认证

    ABP Zero模块通过自定义实现Asp.Net Identity完成身份认证功能, 对Asp.Net Identity做了较大幅度的扩展.同时重写了ABP核心模块中的permission功能,以实现 ...

  8. 【转】ABP源码分析三十二:ABP.SignalR

    Realtime Realtime是ABP底层模块提供的功能,用于管理在线用户.它是使用SignalR实现给在线用户发送通知的功能的前提 IOnlineClient/OnlineClient: 封装在 ...

  9. GRPC golang版源码分析之客户端(二)

    Table of Contents 1. 前言 2. 负载均衡 3. 相关链接 1 前言 前面一篇文章分析了一个grpc call的大致调用流程,顺着源码走了一遍,但是grpc中有一些特性并没有进行分 ...

最新文章

  1. poj3177(双联通分量)
  2. mn之间的回文数c语言,c语言描述回文数的三种算法
  3. handle句柄 matlab_学习随笔之Matlab句柄对象深拷贝方法
  4. 网站数据库服务器怎么启动,启动数据库服务器
  5. [Hnoi2013]消毒
  6. HTCondor运行Java文件
  7. a1在c语言里代指什么意思,A1考试宝典
  8. python传文件_Python实现Windows和Linux之间互相传输文件(文件夹)的方法
  9. 机器学习EPD:概率密度估计
  10. diskmark使用教程
  11. 程序员的自我修养-读后感
  12. “读芯术”精华文章汇总:学术报告
  13. android 微信自动添加好友 项目开源
  14. 校园网络远程登录虚拟机Linux问题解决
  15. air仿QQ弹出提示窗口
  16. oracle 数据分列,oracle怎么按照范围分列!求高手帮忙.
  17. 微信h5页面禁止下拉方法
  18. 大一作业HTML网页作业:中华传统文化题材网页设计(纯html+css实现)科普网
  19. python画图解决Times New Roman自带粗体问题
  20. 工控安全-工控安全案例

热门文章

  1. c linux time微秒_qt linux系统获取当前时间(精确到毫秒、微秒)-Go语言中文社区...
  2. IDEA git修改远程仓库地址
  3. MapXtreme 2005 学习心得 缩放比例下不显示图层(十一)
  4. 关于处理小数点位数的几个oracle函数
  5. ASCII码与字符串的相互转换
  6. Nginx系列~Nginx服务启动不了
  7. trigger 触发器(mysql)
  8. mysql+rsyslog+loganalyzer的部署方案
  9. windows分区的理解
  10. 智能电视系列(4)-高通,天才与极限