ucos 内核成员分析

2024-03-31 06:38
文章标签 分析 成员 内核 ucos

本文主要是介绍ucos 内核成员分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

优先级

ucos3使用64个优先级,每个优先级中可以有多个不同的任务(除了0和末尾优先级)
优先级表初始化过程

void  OS_PrioInit (void)
{CPU_DATA  i;				//无符号整形32for (i = 0u; i < OS_PRIO_TBL_SIZE; i++) {				OSPrioTbl[i] = (CPU_DATA)0;//清空任务表中的任务OS_PRIO_TBL_SIZE=2,相当于只有64个优先级,可以使用cpu函数CPU_CntLeadZeros()快速获取对应的优先级										}
}

系统可以在OSPrioTbl数组中快速获取最高的优先级,通过使用cpu函数CPU_CntLeadZeros()即可快速实现。该函数实现返回数据从左起0的数量,例如对于0x00F01234(32位),则返回8,对于0xFF001234,则返回0,对于32位处理器,优化后的代码为

OS_PRIO OS_PrioGetHightest(void)
{OS_PRIO prio;if(OSPrioTbl[0]!=(OS_PRIO_BIMAP)0){prio=OS_CntLeadZeros(OSPrioTbl[0]);}else{prio=OS_CntLeadZeros(OSPrioTb1[1])+32;}return(prio);
}

在源码中则使用没有优化的函数

//返回高优先级的任务
OS_PRIO  OS_PrioGetHighest (void)
{CPU_DATA  *p_tbl;//32uOS_PRIO    prio;//8uprio  = (OS_PRIO)0;p_tbl = &OSPrioTbl[0];//获取优先级表的位置指针//Os PrioGetHighest()从osPrioTbl[o]开始扫描表,直到找到一个非零的表项为止。循环将总是终止,因为由于空闲任务,表中总是有一个非零的条目。while (*p_tbl == (CPU_DATA)0) {  //循环判断当前优先级状态/* Search the bitmap table for the highest priority       */prio += DEF_INT_CPU_NBR_BITS;//每一等级占32位,判断后一32递增  /* Compute the step of each CPU_DATA entry                */p_tbl++;}//CPU CntLeadzeros()函数只是简单地计算一个CPU数据条目从左开始有多少个零(即最有效位)。例如,假设是32位,OxF0001234会得到0个前导0,Ox0OF01234会得到8个前导0。prio += (OS_PRIO)CPU_CntLeadZeros(*p_tbl);              /* Find the position of the first bit set at the entry    */return (prio);//返回当前闲置的最高优先级
}

就绪列表由两个数据结构组成:一个位图表,用来跟踪哪个优先级已经就绪,另一个表包含每个优先级上所有就绪任务的列表

  • OSPrioTbl[] 存放优先级数组,当某优先级置1,则为闲置优先级
  • OSRdyList[] 准备运行的任务,该数组的每一个位置代表一个优先级,即OSRdyList[0]代表优先级为0的任务,其他的以此类推,而每个数组成员则指向包含(NbrEntries,HeadPtr,TailPtr)参数的地址,NbrEntries代表该优先级中的任务总数,HeadPtr和TailPtr形成一个任务链,该任务链是当前优先级下的所有任务,易得前者指向首部,后者指向尾部。对于OSRdyList[0],将永远只有一个ISR Handler Task,对于OSRdyList[末尾],将永远只用于存放闲置任务。

任务堆栈

先前我们讨论过,ucos的任务堆栈是作为sram前段内存储存在sram中的,之后是设备堆栈,对于任务堆栈,其特性如下图
在这里插入图片描述

会发现作为任务堆栈,其增长方式和栈是一样的(高地址到低地址),堆栈的最下方(最高地址)储存着处理器cpu当前的寄存器值,我们知道ucos是一个抢占式系统,当任务切换时,任务会保存当前的cpu寄存器(CPU Registers)值并进入"休眠",然后切换到另一个优先级高的任务,执行结束后再返回,然后取出休眠前的cpu寄存器值(这些值记录了执行到代码的哪一行,各种临时变量的值和对应的指针),然后把值推回cpu就又能够从先前停下的地方继续运行。存储完这部份数据后的数据就是任务执行需要的其他堆栈数据的位置,没有用的内存值都为0,任务堆栈从高地址到低地址增长,其栈顶由任务的控制块TCB(Task Control Base)的SP指针指定,即标明栈顶。对于TCB,每个TCB都保存着任务初始化时的数据(eg:任务优先级,任务名称,任务状态,内部信息队列等等)。

中断处理

在任务运行过程中,如果遇到了ISR这样中断,系统将停下当前任务去执行中断任务,该中断任务使用的堆栈是MSP(stm32的栈),因此不会破坏原任务的数据结构,在执行结束后返回原任务时,读写任务堆栈中保存的CPU寄存器值就可以继续执行任务。
对于为什么不在ISR中进行任务的切换,网络上的解释是这将可能导致原有的context无法恢复,且如果有多种不同的信息恰好在ISR中释放时,很可能将导致关键信号的接收失败。为此在出现中断嵌套时停止任务切换等操作是更加省时的方式。对于不在ISR中使用任务调度还有的理解如下为什么不能在ISR中进行任务切换;
为了避免在ISR时系统的context被破坏,在ucos中,编写中断服务函数格式如下

void EXTI15_10_IRQHandler(void)
{OSIntEnter();/**裸机时候的中断服务函数**/OSIntExit();
}

其中OSIntEnter()和OSIntExit()代码如下

void  OSIntEnter (void)
{if (OSRunning != OS_STATE_OS_RUNNING) {                 /* Is OS running?                                         */return;                                             /* No                                                     */}if (OSIntNestingCtr >= (OS_NESTING_CTR)250u) {          /* Have we nested past 250 levels?                        */return;                                             /* Yes                                                    */}OSIntNestingCtr++;                                      /* Increment ISR nesting level                            */
}
void  OSIntExit (void)
{CPU_SR_ALLOC();if (OSRunning != OS_STATE_OS_RUNNING) {                 /* Has the OS started?                                    */return;                                             /* No                                                     */}CPU_INT_DIS();if (OSIntNestingCtr == (OS_NESTING_CTR)0) {             /* Prevent OSIntNestingCtr from wrapping                  */CPU_INT_EN();return;}OSIntNestingCtr--;if (OSIntNestingCtr > (OS_NESTING_CTR)0) {              /* ISRs still nested?                                     */CPU_INT_EN();                                       /* Yes                                                    */return;}if (OSSchedLockNestingCtr > (OS_NESTING_CTR)0) {        /* Scheduler still locked?                                */CPU_INT_EN();                                       /* Yes                                                    */return;}OSPrioHighRdy   = OS_PrioGetHighest();                  /* Find highest priority                                  */OSTCBHighRdyPtr = OSRdyList[OSPrioHighRdy].HeadPtr;     /* Get highest priority task ready-to-run                 */if (OSTCBHighRdyPtr == OSTCBCurPtr) {                   /* Current task still the highest priority?               */CPU_INT_EN();                                       /* Yes                                                    */return;}#if OS_CFG_TASK_PROFILE_EN > 0uOSTCBHighRdyPtr->CtxSwCtr++;                            /* Inc. # of context switches for this new task           */
#endifOSTaskCtxSwCtr++;                                       /* Keep track of the total number of ctx switches         */#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS_TaskSw();
#endifOSIntCtxSw();                                           /* Perform interrupt level ctx switch                     */CPU_INT_EN();
}

可以看到,在进出中断服务函数时,会判断当前中断的嵌套层数if (OSIntNestingCtr == (OS_NESTING_CTR)0),一直到中断嵌套层数为0时才会开始执行任务调度,这意味着在出现ISR嵌套时,系统的所有精力都在ISR上,直到所有ISR都完成。如图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-438vfR0b-1616903544917)(/pic\嵌入式\系统\UCOS\ISR_SW.png)]

参考
ucos在M3中的中断嵌套机制
关于STM32-M3/M4的MSP和PSP

任务控制块(TCB)

这是相当多字段的一个结构,结构源码如下

struct os_tcb {CPU_STK             *StkPtr;                            /* Pointer to current top of stack,指向当前已用的栈底     */void                *ExtPtr;                            /* Pointer to user definable data for TCB extension,扩展储存FPU的数据(如果有FPU)*/CPU_STK             *StkLimitPtr;                       /* Pointer used to set stack 'watermark' limit            */OS_TCB              *NextPtr;                           /* Pointer to next     TCB in the TCB list                */OS_TCB              *PrevPtr;                           /* Pointer to previous TCB in the TCB list                */OS_TCB              *TickNextPtr;	OS_TCB              *TickPrevPtr;	/*TickNextPtr,TickPrevPtr这些指针用于在等待时间到期或等待pend调用超时的任务列表中双向链接os tcb。同样,双链表允许从列表中快速插入和删除Os tcb。*/OS_TICK_SPOKE       *TickSpokePtr;                      /* Pointer to tick spoke if task is in the tick list 该指针用于知道任务链接到滴答列表中的哪一项*/CPU_CHAR            *NamePtr;                           /* Pointer to task name                                   */CPU_STK             *StkBasePtr;                        /* Pointer to base address of stack  栈底(任务栈是由高地址到低地址)*/#if defined(OS_CFG_TLS_TBL_SIZE) && (OS_CFG_TLS_TBL_SIZE > 0u)OS_TLS               TLS_Tbl[OS_CFG_TLS_TBL_SIZE];
#endifOS_TASK_PTR          TaskEntryAddr;                     /* Pointer to task entry point address任务函数代码的位置*/void                *TaskEntryArg;                      /* Argument passed to task when it was created任务函数代码使用的参数*/OS_PEND_DATA        *PendDataTblPtr;                    /* Pointer to list containing objects pended on           */OS_STATE             PendOn;                            /* Indicates what task is pending on 表明等待的对象类型(标志,信号量,互斥锁...)                     */OS_STATUS            PendStatus;                        /* Pend status 表明等待的结果(ok,del,timeout,about)				*/OS_STATE             TaskState;                         /* See OS_TASK_STATE_xxx 当前任务状态(挂起,运行,删除....)*/OS_PRIO              Prio;                              /* Task priority (0 == highest)                           */CPU_STK_SIZE         StkSize;                           /* Size of task stack (in number of stack elements) 整个任务堆栈的大小*/OS_OPT               Opt;                               /* Task options as passed by OSTaskCreate()               */OS_OBJ_QTY           PendDataTblEntries;                /* Size of array of objects to pend on任务正在等待的对象数量*/CPU_TS               TS;                                /* Timestamp                                              */OS_SEM_CTR           SemCtr;                            /* Task specific semaphore counter 任务收到的信号量的数量 *//* DELAY / TIMEOUT                                        */OS_TICK              TickCtrPrev;                       /* Previous time when task was            ready           */OS_TICK              TickCtrMatch;                      /* Absolute time when task is going to be ready           */OS_TICK              TickRemain;                        /* Number of ticks remaining for a match (updated at ...  *//* ... run-time by OS_StatTask() 剩余的等待时间           */OS_TICK              TimeQuanta;	OS_TICK              TimeQuantaCtr;												/*这些字段用于时间切片。当多个任务以相同优先级准备运行时,. timequanta决定任务将执行多少时间(以ticks为单位),直到它被uC/OS-III抢占,以便下一个具有相同优先级的任务有机会执行。.TimeQuantactr将跟踪此任务发生的剩余滴答数,并在任务的时间片开始时由.TimeQuanta加载*/
#if OS_MSG_EN > 0uvoid                *MsgPtr;                            /* Message received                                       */OS_MSG_SIZE          MsgSize;
#endif#if OS_CFG_TASK_Q_EN > 0uOS_MSG_Q             MsgQ;                              /* Message queue associated with task                     */
#if OS_CFG_TASK_PROFILE_EN > 0uCPU_TS               MsgQPendTime;                      /* Time it took for signal to be received 收到信息等待的时间*/CPU_TS               MsgQPendTimeMax;                   /* Max amount of time it took for signal to be received   */
#endif
#endif#if OS_CFG_TASK_REG_TBL_SIZE > 0uOS_REG               RegTbl[OS_CFG_TASK_REG_TBL_SIZE];  /* Task specific registers(可以用来储存任务相关的信息)    */
#endif#if OS_CFG_FLAG_EN > 0uOS_FLAGS             FlagsPend;                         /* Event flag(s) to wait on 任务挂起的事件标志		        */OS_FLAGS             FlagsRdy;                          /* Event flags that made task ready to run                */OS_OPT               FlagsOpt;                          /* Options (See OS_OPT_FLAG_xxx)当任务挂起事件标志时,此字段包含pend的类型*/
#endif#if OS_CFG_TASK_SUSPEND_EN > 0uOS_NESTING_CTR       SuspendCtr;                        /* Nesting counter for OSTaskSuspend()记录任务被挂起的时间(可以嵌套)*/
#endif#if OS_CFG_TASK_PROFILE_EN > 0uOS_CPU_USAGE         CPUUsage;                          /* CPU Usage of task (0.00-100.00%)CPUUsage包含任务的CPU使用率百分比(0到100%)。*/OS_CPU_USAGE         CPUUsageMax;                       /* CPU Usage of task (0.00-100.00%) - Peak                */OS_CTX_SW_CTR        CtxSwCtr;                          /* Number of time the task was switched in记录任务运行的次数*/CPU_TS               CyclesDelta;                       /* value of OS_TS_GET() - .CyclesStart记录当前的时间戳    */CPU_TS               CyclesStart;                       /* Snapshot of cycle counter at start of task resumption切换到当前任务时的时间戳*/OS_CYCLES            CyclesTotal;                       /* Total number of # of cycles the task has been running任务总运行时间*/OS_CYCLES            CyclesTotalPrev;                   /* Snapshot of previous # of cycles上一次运行的时间?     */CPU_TS               SemPendTime;                       /* Time it took for signal to be received 接收到信号量的时间*/CPU_TS               SemPendTimeMax;                    /* Max amount of time it took for signal to be received   */
#endif#if OS_CFG_STAT_TASK_STK_CHK_EN > 0uCPU_STK_SIZE         StkUsed;                           /* Number of stack elements used from the stack已用应用堆栈           */CPU_STK_SIZE         StkFree;                           /* Number of stack elements free on   the stack空闲应用堆栈           */
#endif#ifdef CPU_CFG_INT_DIS_MEAS_ENCPU_TS               IntDisTimeMax;                     /* Maximum interrupt disable time                         */
#endif
#if OS_CFG_SCHED_LOCK_TIME_MEAS_EN > 0uCPU_TS               SchedLockTimeMax;                  /* Maximum scheduler lock time该字段跟踪任务的最大调度程序锁定时间。  */
#endif#if OS_CFG_DBG_EN > 0uOS_TCB              *DbgPrevPtr;OS_TCB              *DbgNextPtr;CPU_CHAR            *DbgNamePtr;									//包含导致任务挂起的对象类型(信号量,互斥锁,消息队列...)
#endif
};

这篇关于ucos 内核成员分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/863668

相关文章

Mysql的主从同步/复制的原理分析

《Mysql的主从同步/复制的原理分析》:本文主要介绍Mysql的主从同步/复制的原理分析,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录为什么要主从同步?mysql主从同步架构有哪些?Mysql主从复制的原理/整体流程级联复制架构为什么好?Mysql主从复制注意

java -jar命令运行 jar包时运行外部依赖jar包的场景分析

《java-jar命令运行jar包时运行外部依赖jar包的场景分析》:本文主要介绍java-jar命令运行jar包时运行外部依赖jar包的场景分析,本文给大家介绍的非常详细,对大家的学习或工作... 目录Java -jar命令运行 jar包时如何运行外部依赖jar包场景:解决:方法一、启动参数添加: -Xb

C++/类与对象/默认成员函数@构造函数的用法

《C++/类与对象/默认成员函数@构造函数的用法》:本文主要介绍C++/类与对象/默认成员函数@构造函数的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录名词概念默认成员函数构造函数概念函数特征显示构造函数隐式构造函数总结名词概念默认构造函数:不用传参就可以

C++类和对象之默认成员函数的使用解读

《C++类和对象之默认成员函数的使用解读》:本文主要介绍C++类和对象之默认成员函数的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、默认成员函数有哪些二、各默认成员函数详解默认构造函数析构函数拷贝构造函数拷贝赋值运算符三、默认成员函数的注意事项总结一

Apache 高级配置实战之从连接保持到日志分析的完整指南

《Apache高级配置实战之从连接保持到日志分析的完整指南》本文带你从连接保持优化开始,一路走到访问控制和日志管理,最后用AWStats来分析网站数据,对Apache配置日志分析相关知识感兴趣的朋友... 目录Apache 高级配置实战:从连接保持到日志分析的完整指南前言 一、Apache 连接保持 - 性

Linux中的more 和 less区别对比分析

《Linux中的more和less区别对比分析》在Linux/Unix系统中,more和less都是用于分页查看文本文件的命令,但less是more的增强版,功能更强大,:本文主要介绍Linu... 目录1. 基础功能对比2. 常用操作对比less 的操作3. 实际使用示例4. 为什么推荐 less?5.

spring-gateway filters添加自定义过滤器实现流程分析(可插拔)

《spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔)》:本文主要介绍spring-gatewayfilters添加自定义过滤器实现流程分析(可插拔),本文通过实例图... 目录需求背景需求拆解设计流程及作用域逻辑处理代码逻辑需求背景公司要求,通过公司网络代理访问的请求需要做请

Java集成Onlyoffice的示例代码及场景分析

《Java集成Onlyoffice的示例代码及场景分析》:本文主要介绍Java集成Onlyoffice的示例代码及场景分析,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 需求场景:实现文档的在线编辑,团队协作总结:两个接口 + 前端页面 + 配置项接口1:一个接口,将o

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

Dubbo之SPI机制的实现原理和优势分析

《Dubbo之SPI机制的实现原理和优势分析》:本文主要介绍Dubbo之SPI机制的实现原理和优势,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Dubbo中SPI机制的实现原理和优势JDK 中的 SPI 机制解析Dubbo 中的 SPI 机制解析总结Dubbo中