μCOS实时操作系统笔记

来源:互联网 发布:狗头表情软件 编辑:程序博客网 时间:2024/05/16 12:29

任务优先级数值越低,优先级越高。

任务的调度方式

传统的Unix是基于优先级调度的,确保每个进程都能运行,低优先级的也不会被饿死。在每个时钟tick会调用最高优先级的任务,而每个任务的优先级有一个计算方式在每次tick中计算。优先级=用户优先级+执行时间-衰减因子(等待时间越长,优先级越高)+nice值(对其他用户友好)。程序占用CPU太长时间会降低优先级,等待时间太长则增加优先级。 
优先级反转:当高优先级的任务需要的资源被低优先级任务持有而阻塞不能运行时,有优先级比占有资源的低优先级任务稍高的进程占用着CPU,此时资源将永远不会被释放。因为应该临时把需要资源这两个任务的优先级交换,使需要资源的更高优先级任务能够得到调度。 
对于实时操作系统支持多种调度方式同时运行,可以在基于优先级的基础上使用硬件中端直接响应。 

μCOS操作系统的调度方式:

对于μCOS每个任务对应一个优先级,利用优先级来对任务进行各种操作(删除,延时,等待等)。

了解调度机制首先必须认识μCOS的数据结构。

TCB(TASK CONTROL BLOCK):任务基础结构。堆栈指针位置及大小、数据消息事件指针、任务状态及优先级、任务运行信息。

  1. typedef struct os_tcb {
  2. OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */
  3. #if OS_TASK_CREATE_EXT_EN > 0u
  4. void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */
  5. OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */
  6. INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */
  7. INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */
  8. INT16U OSTCBId; /* Task ID (0..65535) */
  9. #endif
  10. struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */
  11. struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */
  12. #if (OS_EVENT_EN)
  13. OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
  14. #endif
  15. #if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u)
  16. OS_EVENT **OSTCBEventMultiPtr; /* Pointer to multiple event control blocks */
  17. #endif
  18. #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u)
  19. void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */
  20. #endif
  21. #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u)
  22. #if OS_TASK_DEL_EN > 0u
  23. OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */
  24. #endif
  25. OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */
  26. #endif
  27. INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */
  28. INT8U OSTCBStat; /* Task status */
  29. INT8U OSTCBStatPend; /* Task PEND status */
  30. INT8U OSTCBPrio; /* Task priority (0 == highest) */
  31. INT8U OSTCBX; /* Bit position in group corresponding to task priority */
  32. INT8U OSTCBY; /* Index into ready table corresponding to task priority */
  33. OS_PRIO OSTCBBitX; /* Bit mask to access bit position in ready table */
  34. OS_PRIO OSTCBBitY; /* Bit mask to access bit position in ready group */
  35. #if OS_TASK_DEL_EN > 0u
  36. INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */
  37. #endif
  38. #if OS_TASK_PROFILE_EN > 0u
  39. INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */
  40. INT32U OSTCBCyclesTot; /* Total number of clock cycles the task has been running */
  41. INT32U OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption */
  42. OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */
  43. INT32U OSTCBStkUsed; /* Number of bytes used from the stack */
  44. #endif
  45. #if OS_TASK_NAME_EN > 0u
  46. INT8U *OSTCBTaskName;
  47. #endif
  48. #if OS_TASK_REG_TBL_SIZE > 0u
  49. INT32U OSTCBRegTbl[OS_TASK_REG_TBL_SIZE];
  50. #endif
  51. } OS_TCB;

任务控制块实体声明如下OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS],即创建最大任务处个任务控制数据块,数组下标对应程序优先级。 
空闲链表OS_TCB *OSTCBFreeList,操作系统初始化时指向OSTCBTbl[0],将OSTCBNext指针指向OSTCBTbl[1],类推往后。创建新任务时需要在空闲链表中找到一个任务控制块。 
就绪链表OS_TCB *OSTCBList,操作系统创建任务的时候讲任务从空闲链表移除,在就绪链表里添加。系统会有一个空闲任务,通过配置还可以有一个统计任务,统计任务可以查看CPU的利用率。 
任务优先级链表OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1]之所以还要优先级链表是因为假设创建优先级为5的任务,去任务控制块地址后需要简单地把值给OSTCBPrioTbl[5]。以后查询任务控制块的时候就不需要遍历就绪表,直接用优先级对应地址,节约时间。 
任务堆栈

  1. #define TASK_STK_SIZE 512
  2. typedef unsigned int OS_STK;
  3. OS_STK TaskStk[OS_MAX_TASKS][TASK_STK_SIZE]

任务就绪表和就绪组

  1. #if OS_LOWEST_PRIO <= 63u
  2. typedef INT8U OS_PRIO;
  3. #else
  4. typedef INT16U OS_PRIO;
  5. #endif
  6. OS_PRIO OSRdyGrp; /* Ready list group */
  7. OS_PRIO OSRdyTbl[OS_RDY_TBL_SIZE]; /* Table of tasks which are ready to run */

就绪组是任务的高位(prio/8 or prio >> 3)同时做就绪表下标,就绪表中存放的是对应就绪组的低位。操作系统通过操作就绪表和就绪组控制任务是否会被调度(任务调度是从就就绪表中选取最高优先级执行)。 
优先级映射表INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};相当于38译码器。 
最高优先级查找表INT8U const OSUnMapTbl[],作用是用内存换性能,直接通过查表取出当前优先级高位和低位部分最小有效优先级。

  1. y = OSUnMapTbl[OSRdyGrp];
  2. OSPrioHIGHrDY = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]);
通过任务调度的数据结构我们就可以大概知道μCOS的任务调度方式,也是弄懂调度方式的基础。
小结:μCOS操作系统任务调度有系统systick中断服务程序(时钟嘀嗒更新、延时任务更新、任务调度),任务调度时需要将旧任务变量保存到旧任务自己的堆栈中去,查找就绪表将最高优先级任务运行状态从堆栈中弹出执行。
任务有五个基本状态:睡眠态、就绪态、运行态、阻塞态、挂起态。

其中睡眠态任务尚未加入就绪表或者已经从就绪表中删除后,将整个任务终止;就绪态为任务做好准备可以被调度;运行态即任务优先级足够高被调度运行占用CPU;阻塞态是任务被系统延时或者等待资源暂停状态; 挂起态是系统发生中断时,跳转到中断服务程序中执行(即单片机无操作系统的编程中的中断)。

事件管理

消息邮箱、消息队列、信号量、互斥信号量、事件标志组

和任务管理一样,事件管理也有自己的结构体数据结构,其中包含了事件类型、结构体指针、事件技术、事件组及事件表、事件名称。并且同任务一样会初始化事件空闲列表,并在用的时候加入事件组&事件表中。同任务调度一样,当发生某一时间后再此类事件的事件组&表中选择最高优先级的事件任务,使之恢复就绪态(其余认为阻塞状态)。事件一般使用为先创建,一个任务发送一个任务查询和阻塞等待事件发生。一般有如下函数(以下为信号量):

  1. OS_EVENT *OSSemCreate (INT16U cnt); //创建事件
  2. OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr); //删除事件
  3. void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr); //阻塞自己等待事件发生
  4. INT8U OSSemPendAbort(OS_EVENT *pevent, INT8U opt, INT8U *perr); //放弃等待
  5. INT8U OSSemPost (OS_EVENT *pevent); //发出事件
  6. INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA p_sem_data); //查询信号量的信息
  7. void OSSemSet (OS_EVENT *pevent, INT16U cnt, INT8U *perr); //设置信号量的值
  8. INT16U OSSemAccept (OS_EVENT *pevent); //查询事件,不阻塞自己

内存管理

系统分成多个分区,每个分区又包含过个大小相同的内存块

事件上内存分区就是二维数组INT32U MemBuf[10][20]就是一个分区分为10个内存块,每个大小20字节。再通过结构体指针管理分区。

总结:
关于μCOS
  • Tips:μCOS的钩子函数可用于调试,且统计任务的钩子函数可用于根据CPU符合动态调节CPU运行频率
μCOS的优点
  • 任务调度的时候采用数组映射提高了查找最高优先级效率
  • 实时性好(实时程序可采用中断处理,加之优先级调度)
  • 开源,可裁剪配置
  • 互斥量、信号量、信号
μCOS的缺点
  • 支持任务数量少 (μCOS-II可用64-8)
  • 优先级反转实现方法并不是很好(多占用一个预定义优先级)
  • 优先级调度方法可能会使部分低优先级程序饿死
  • 优先级 = 用户优先级+执行时间-衰减因子(等待时间越长,优先级越高)
  • 不支持相同优先级的时间片轮转

0 0
原创粉丝点击