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

相关文章

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

kotlin中const 和val的区别及使用场景分析

《kotlin中const和val的区别及使用场景分析》在Kotlin中,const和val都是用来声明常量的,但它们的使用场景和功能有所不同,下面给大家介绍kotlin中const和val的区别,... 目录kotlin中const 和val的区别1. val:2. const:二 代码示例1 Java

Go标准库常见错误分析和解决办法

《Go标准库常见错误分析和解决办法》Go语言的标准库为开发者提供了丰富且高效的工具,涵盖了从网络编程到文件操作等各个方面,然而,标准库虽好,使用不当却可能适得其反,正所谓工欲善其事,必先利其器,本文将... 目录1. 使用了错误的time.Duration2. time.After导致的内存泄漏3. jsO

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3

新特性抢先看! Ubuntu 25.04 Beta 发布:Linux 6.14 内核

《新特性抢先看!Ubuntu25.04Beta发布:Linux6.14内核》Canonical公司近日发布了Ubuntu25.04Beta版,这一版本被赋予了一个活泼的代号——“Plu... Canonical 昨日(3 月 27 日)放出了 Beta 版 Ubuntu 25.04 系统镜像,代号“Pluc

找不到Anaconda prompt终端的原因分析及解决方案

《找不到Anacondaprompt终端的原因分析及解决方案》因为anaconda还没有初始化,在安装anaconda的过程中,有一行是否要添加anaconda到菜单目录中,由于没有勾选,导致没有菜... 目录问题原因问http://www.chinasem.cn题解决安装了 Anaconda 却找不到 An

Spring定时任务只执行一次的原因分析与解决方案

《Spring定时任务只执行一次的原因分析与解决方案》在使用Spring的@Scheduled定时任务时,你是否遇到过任务只执行一次,后续不再触发的情况?这种情况可能由多种原因导致,如未启用调度、线程... 目录1. 问题背景2. Spring定时任务的基本用法3. 为什么定时任务只执行一次?3.1 未启用

C++ 各种map特点对比分析

《C++各种map特点对比分析》文章比较了C++中不同类型的map(如std::map,std::unordered_map,std::multimap,std::unordered_multima... 目录特点比较C++ 示例代码 ​​​​​​代码解释特点比较1. std::map底层实现:基于红黑

Spring、Spring Boot、Spring Cloud 的区别与联系分析

《Spring、SpringBoot、SpringCloud的区别与联系分析》Spring、SpringBoot和SpringCloud是Java开发中常用的框架,分别针对企业级应用开发、快速开... 目录1. Spring 框架2. Spring Boot3. Spring Cloud总结1. Sprin

Spring 中 BeanFactoryPostProcessor 的作用和示例源码分析

《Spring中BeanFactoryPostProcessor的作用和示例源码分析》Spring的BeanFactoryPostProcessor是容器初始化的扩展接口,允许在Bean实例化前... 目录一、概览1. 核心定位2. 核心功能详解3. 关键特性二、Spring 内置的 BeanFactory