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

相关文章

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

最长公共子序列问题的深度分析与Java实现方式

《最长公共子序列问题的深度分析与Java实现方式》本文详细介绍了最长公共子序列(LCS)问题,包括其概念、暴力解法、动态规划解法,并提供了Java代码实现,暴力解法虽然简单,但在大数据处理中效率较低,... 目录最长公共子序列问题概述问题理解与示例分析暴力解法思路与示例代码动态规划解法DP 表的构建与意义动

C#使用DeepSeek API实现自然语言处理,文本分类和情感分析

《C#使用DeepSeekAPI实现自然语言处理,文本分类和情感分析》在C#中使用DeepSeekAPI可以实现多种功能,例如自然语言处理、文本分类、情感分析等,本文主要为大家介绍了具体实现步骤,... 目录准备工作文本生成文本分类问答系统代码生成翻译功能文本摘要文本校对图像描述生成总结在C#中使用Deep

Redis主从/哨兵机制原理分析

《Redis主从/哨兵机制原理分析》本文介绍了Redis的主从复制和哨兵机制,主从复制实现了数据的热备份和负载均衡,而哨兵机制可以监控Redis集群,实现自动故障转移,哨兵机制通过监控、下线、选举和故... 目录一、主从复制1.1 什么是主从复制1.2 主从复制的作用1.3 主从复制原理1.3.1 全量复制

Redis主从复制的原理分析

《Redis主从复制的原理分析》Redis主从复制通过将数据镜像到多个从节点,实现高可用性和扩展性,主从复制包括初次全量同步和增量同步两个阶段,为优化复制性能,可以采用AOF持久化、调整复制超时时间、... 目录Redis主从复制的原理主从复制概述配置主从复制数据同步过程复制一致性与延迟故障转移机制监控与维

Redis连接失败:客户端IP不在白名单中的问题分析与解决方案

《Redis连接失败:客户端IP不在白名单中的问题分析与解决方案》在现代分布式系统中,Redis作为一种高性能的内存数据库,被广泛应用于缓存、消息队列、会话存储等场景,然而,在实际使用过程中,我们可能... 目录一、问题背景二、错误分析1. 错误信息解读2. 根本原因三、解决方案1. 将客户端IP添加到Re

Linux内核之内核裁剪详解

《Linux内核之内核裁剪详解》Linux内核裁剪是通过移除不必要的功能和模块,调整配置参数来优化内核,以满足特定需求,裁剪的方法包括使用配置选项、模块化设计和优化配置参数,图形裁剪工具如makeme... 目录简介一、 裁剪的原因二、裁剪的方法三、图形裁剪工具四、操作说明五、make menuconfig

Redis主从复制实现原理分析

《Redis主从复制实现原理分析》Redis主从复制通过Sync和CommandPropagate阶段实现数据同步,2.8版本后引入Psync指令,根据复制偏移量进行全量或部分同步,优化了数据传输效率... 目录Redis主DodMIK从复制实现原理实现原理Psync: 2.8版本后总结Redis主从复制实

锐捷和腾达哪个好? 两个品牌路由器对比分析

《锐捷和腾达哪个好?两个品牌路由器对比分析》在选择路由器时,Tenda和锐捷都是备受关注的品牌,各自有独特的产品特点和市场定位,选择哪个品牌的路由器更合适,实际上取决于你的具体需求和使用场景,我们从... 在选购路由器时,锐捷和腾达都是市场上备受关注的品牌,但它们的定位和特点却有所不同。锐捷更偏向企业级和专

如何安装HWE内核? Ubuntu安装hwe内核解决硬件太新的问题

《如何安装HWE内核?Ubuntu安装hwe内核解决硬件太新的问题》今天的主角就是hwe内核(hardwareenablementkernel),一般安装的Ubuntu都是初始内核,不能很好地支... 对于追求系统稳定性,又想充分利用最新硬件特性的 Ubuntu 用户来说,HWEXBQgUbdlna(Har