中文教材:
基本概念:硬件(不考),任务和约束,定义调度的问题,调度的异常
实时任务调度和调度分析:非周期任务调度,周期任务调度
μC/OS:任务调度,中断和时间管理,事件管理,消息管理,内存管理
英文教材:
资源控制协议:优先级反转,非抢占协议,优先级继承协议,优先级封顶协议,对协议进行分析
过载处理:处理非周期过载,处理溢出,处理恒定过载
软实时,硬实时,准实时
嵌入式应用
实时:定量(quantitative),定性(qualitative)
实时系统:给定的时间约束下面完成任务的计算机系统
实时操作系统:对外界输入有限时响应,根据实时进行设计,帮助任务满足时间要求
安全与可用(safety & reliability)
失败安全状态(fail safe state)
安全关键系统,任何失败都会对系统造成严重损坏,没有失败安全状态,只能通过可靠性提高来保证安全
设计高可靠系统:
任务Task:到达时间ai,开始时间si,计算时间ci,结束时间fi,响应时间fi - si
就绪队列ready queue
抢占preemption
调度schedule
任务状态task states
实时任务real-time task
作业job:任务的实例
启动模式Activation Mode :
周期任务:
非周期任务:\(a_{i,k+1} > a_{i,k}\)
零星任务Sporadic:\(a_{i,k+1} \ge a_{i,k} + Ti\)
重要程度:硬实时HARD task,准实时FIRMtask,软实时SOFT task
抖动Jitter:衡量周期事件上时间的变化
绝对absolute:\(\underset k \max(t_k - a_k) - \underset k \min(t_k - a_k)\)
相对relative:\(\underset k \max | (t_k - a_k) - (t_{k-1} - a_{k-1})|\)
类型:结束时间抖动finishing-time jitter,启动时间抖动start-time jitter, 完成时间抖动completion-time jitter(I/O jitter)
参数分类parameters:
空闲IDLE
类型
并发Concurrency
并发与I/O
周期任务:
冲突:互斥信号量,信号量
先后次序
处理器速度快!=实时性能好 fast !=realtime
延迟delay
Semaphores Semaphores
多任务
任务状态
可重入与不可重入
函数可重入是指一个函数可以被多个任务调用, 而不需要担心在
任务切换的过程中, 代码的执行会产生错误的结果
内核是操作系统最核心的部分, 其主要功能就是进行任务调度
在μC/OS-II中, 可以同时有64个就绪任务, 每个任务都有各自的优先级。 优先级用无符号整数来表示, 从0到63,数字越大则优先级越低。
μC/OS总是调度就绪了的、 优先级最高的任务获得CPU的控制权, 不管这个任务是什么, 执行什么样的功能, 也不管该任务是否已经等了很久。
其实就是任务是否可抢占
有时候一个任务完成的前提是需要另一个任务给出一个结果, 任务之间的这种制约性的合作运行机制叫做任务间的同步。
共享资源称为临界资源(Critical Resource) 。这种访问共享资源的排他性就是互斥。
每个任务中访问共享资源的那段程序称为临界区(Critical Section) , 因为共享资源的访问是要互斥的。 在临界区不允许任务切换, 这是最根本的原则。
事件标志组管理的条件组合可以是多个事件都发生,也可以是多个事件中有任何一个事件发生。 尤其特别的是, 还可以是多个事件都没有发生或多个事件中有任何一个事件没有发生。
消息队列实质上是消息邮箱的队列
μC/OS-II中, 采用分区的方式管理内存, 即将连续的大块内存按分区来管理, 每个系统中有数个这样的分区,每个分区又包含数个内存块, 每个内存块大小相同。
1.什么是操作系统, 什么是实时操作系统, 实时操作系统
应该具有哪些特性?
§ 2.什么是任务, 任务和程序有什么区别? 任务都有哪些状
态?
§ 3.编写一个可重入函数, 实现将整数转换字符串。 说明为
什么该函数是可重入的。
§ 4.什么是不可剥夺内核和可剥夺内核, μC/OS为什么采用
可剥夺内核?
§ 5.操作系统中的事件管理都包括哪些, 并一一加以论述。
任务管理的数据结构包括任务控制块, 任务空闲链表和任务就绪链表, 任务优先级指针表, 任务堆栈等, 是μC/OS-II内核的核心部分之一。
任务控制块是任务管理的核心数据结构, 操作系统在启动的时候, 首先要在内存中创建一定是数量的任务控制块。 任务控制块的最大数量等于操作系统能同时管理的最多任务数 。
typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */ #if OS_TASK_CREATE_EXT_EN > 0u void *OSTCBExtPtr; /* Pointer to user definable data for TCB extension */ OS_STK *OSTCBStkBottom; /* Pointer to bottom of stack */ INT32U OSTCBStkSize; /* Size of task stack (in number of stack elements) */ INT16U OSTCBOpt; /* Task options as passed by OSTaskCreateExt() */ INT16U OSTCBId; /* Task ID (0..65535) */ #endif struct os_tcb *OSTCBNext; /* Pointer to next TCB in the TCB list */ struct os_tcb *OSTCBPrev; /* Pointer to previous TCB in the TCB list */ #if (OS_EVENT_EN) OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */ #endif #if (OS_EVENT_EN) && (OS_EVENT_MULTI_EN > 0u) OS_EVENT **OSTCBEventMultiPtr; /* Pointer to multiple event control blocks */ #endif #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u) void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */ #endif #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) #if OS_TASK_DEL_EN > 0u OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */ #endif OS_FLAGS OSTCBFlagsRdy; /* Event flags that made task ready to run */ #endif INT32U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */ INT8U OSTCBStat; /* Task status */ INT8U OSTCBStatPend; /* Task PEND status */ INT8U OSTCBPrio; /* Task priority (0 == highest) */ INT8U OSTCBX; /* 任务优先级低3位 */ INT8U OSTCBY; /* 任务优先级右移3位,相当于优先级除以8 */ OS_PRIO OSTCBBitX; /* 任务优先级在对应的任务就绪表中的位置 */ OS_PRIO OSTCBBitY; /* 任务在优先级组表中的位置 */ #if OS_TASK_DEL_EN > 0u INT8U OSTCBDelReq; /* Indicates whether a task needs to delete itself */ #endif #if OS_TASK_PROFILE_EN > 0u INT32U OSTCBCtxSwCtr; /* Number of time the task was switched in */ INT32U OSTCBCyclesTot; /* Total number of clock cycles the task has been running */ INT32U OSTCBCyclesStart; /* Snapshot of cycle counter at start of task resumption */ OS_STK *OSTCBStkBase; /* Pointer to the beginning of the task stack */ INT32U OSTCBStkUsed; /* Number of bytes used from the stack */ #endif #if OS_TASK_NAME_EN > 0u INT8U *OSTCBTaskName; #endif #if OS_TASK_REG_TBL_SIZE > 0u INT32U OSTCBRegTbl[OS_TASK_REG_TBL_SIZE]; #endif INT32U deadline; struct os_tcb *rdyList; } OS_TCB;
任务控制块实体的声明如下:
OS_TCB OSTCBTbl[OS_MAX_TASKS + OS_N_SYS_TASKS]
OS_MAX_TASKS为最多的用户任务数,OS_N_SYS_TASKS为系统任务数, 一般情况下为2。ucos_ii最多可以有64个任务
μC/OS将任务控制块划分为两个链表, 就绪链表和空闲链表
系统有一个OSTCBFreeList指针指向第一个空闲链表,空闲链表只用到next,是单向链表。每当取走一个空闲链表项,指针后移。空闲表项加入OSTCBList且prev指向之前OSTCBList,OSTCBList指向的是最后一个表项。
任务优先级指针表也就是任务优先级指针数组, 在μC/OS-II任务管理中频繁使用,代码中随处可见。 它是用来获取某优先级的任务的任务控制块地址。 它的定义为:
OS_TCB *OSTCBPrioTbl[OS_LOWEST_PRIO + 1]
OS_LOWEST_PRIO为最低优先级的任务的优先级, 因为低优先级的任务数值最大, 而任务优先级是从0开始的, 所以其实OS_LOWEST_PRIO + 1就是任务的数量。
如果堆栈是向下增长, 也就是从高地址向低地址增长, 那么在任务刚开始创建后, 堆栈是空的。 相反, 如果堆栈是向上增长的, 栈顶在为TaskStk[0]。
简单的对应关系,空闲任务63,统计任务62。
设置任务就绪:
OSRdyGrp |= OSMapTbl[prio>>3]; OSRdyTbl[prio>>3] |= OSMapTbl[prio&0x07]; INT8U const OSMapTbl[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};
获取最高优先级
y = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)(y<<3) + OSUnMapTbl[OSRdyTbl[y]]); INT8U const OSUnMapTbl[] = {...};
INT8U OS_TCBInit (INT8U prio, //被创建任务的优先级 OS_STK *ptos, //任务堆栈栈顶的地址 OS_STK *pbos, //任务堆栈栈底的地址,如果不使用扩展,即不进行堆栈检查,就传NULL INT16U id, //任务的ID,16位 INT32U stk_size, //堆栈的大小 void *pext, //任务控制块的扩展块的地址 INT16U opt); //其他选项
INT8U OS_TCBInit (INT8U prio, OS_STK *ptos, OS_STK *pbos, INT16U id, INT32U stk_size, void *pext, INT16U opt) { OS_TCB *ptcb; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #if OS_TASK_REG_TBL_SIZE > 0u INT8U i; #endif OS_ENTER_CRITICAL(); //进入临界区,关中断 ptcb = OSTCBFreeList; /* Get a free TCB from the free TCB list */ if (ptcb != (OS_TCB *)0) { OSTCBFreeList = ptcb->OSTCBNext; /* Update pointer to free TCB list */ OS_EXIT_CRITICAL(); ptcb->OSTCBStkPtr = ptos; /* Load Stack pointer in TCB */ ptcb->OSTCBPrio = prio; /* Load task priority into TCB */ ptcb->OSTCBStat = OS_STAT_RDY; /* Task is ready to run */ ptcb->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ ptcb->OSTCBDly = 0u; /* Task is not delayed */ #if OS_TASK_CREATE_EXT_EN > 0u ptcb->OSTCBExtPtr = pext; /* Store pointer to TCB extension */ ptcb->OSTCBStkSize = stk_size; /* Store stack size */ ptcb->OSTCBStkBottom = pbos; /* Store pointer to bottom of stack */ ptcb->OSTCBOpt = opt; /* Store task options */ ptcb->OSTCBId = id; /* Store task ID */ #else pext = pext; /* Prevent compiler warning if not used */ stk_size = stk_size; pbos = pbos; opt = opt; id = id; #endif #if OS_TASK_DEL_EN > 0u ptcb->OSTCBDelReq = OS_ERR_NONE; #endif #if OS_LOWEST_PRIO <= 63u /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)(prio >> 3u); ptcb->OSTCBX = (INT8U)(prio & 0x07u); #else /* Pre-compute X, Y */ ptcb->OSTCBY = (INT8U)((INT8U)(prio >> 4u) & 0xFFu); ptcb->OSTCBX = (INT8U) (prio & 0x0Fu); #endif /* Pre-compute BitX and BitY */ ptcb->OSTCBBitY = (OS_PRIO)(1uL << ptcb->OSTCBY); ptcb->OSTCBBitX = (OS_PRIO)(1uL << ptcb->OSTCBX); #if (OS_EVENT_EN) ptcb->OSTCBEventPtr = (OS_EVENT *)0; /* Task is not pending on an event */ #if (OS_EVENT_MULTI_EN > 0u) ptcb->OSTCBEventMultiPtr = (OS_EVENT **)0; /* Task is not pending on any events */ #endif #endif #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) && (OS_TASK_DEL_EN > 0u) ptcb->OSTCBFlagNode = (OS_FLAG_NODE *)0; /* Task is not pending on an event flag */ #endif #if (OS_MBOX_EN > 0u) || ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) ptcb->OSTCBMsg = (void *)0; /* No message received */ #endif #if OS_TASK_PROFILE_EN > 0u ptcb->OSTCBCtxSwCtr = 0uL; /* Initialize profiling variables */ ptcb->OSTCBCyclesStart = 0uL; ptcb->OSTCBCyclesTot = 0uL; ptcb->OSTCBStkBase = (OS_STK *)0; ptcb->OSTCBStkUsed = 0uL; #endif #if OS_TASK_NAME_EN > 0u ptcb->OSTCBTaskName = (INT8U *)(void *)"?"; #endif #if OS_TASK_REG_TBL_SIZE > 0u /* Initialize the task variables */ for (i = 0u; i < OS_TASK_REG_TBL_SIZE; i++) { ptcb->OSTCBRegTbl[i] = 0u; } #endif OSTCBInitHook(ptcb); OSTaskCreateHook(ptcb); /* Call user defined hook */ OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = ptcb; ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */ ptcb->OSTCBPrev = (OS_TCB *)0; if (OSTCBList != (OS_TCB *)0) { OSTCBList->OSTCBPrev = ptcb; } OSTCBList = ptcb; OSRdyGrp |= ptcb->OSTCBBitY; /* Make task ready to run */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OSTaskCtr++; /* Increment the #tasks counter */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); } OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NO_MORE_TCB); }
OS_ENTER_CRITICAL();
OSTCBPrioTbl[prio] = ptcb;
ptcb->OSTCBNext = OSTCBList; /* Link into TCB chain */
ptcb->OSTCBPrev = (OS_TCB *)0;
if (OSTCBList != (OS_TCB )0) {
OSTCBList->OSTCBPrev = ptcb;
}
OSTCBList = ptcb;
OSRdyGrp |= ptcb->OSTCBBitY; / Make task ready to run /
OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX;
OSTaskCtr++; / Increment the #tasks counter */
OS_EXIT_CRITICAL();
操作系统初始化函数OS_INIT是操作系统在开始运行的最初, 对全局变量、 任务控制块、 就绪表、 事件及消息队列等重要数据结构进行的初始化操作,并创建空闲任务、 统计任务等系统任务。 该函数必须在创建用户对象及调用OSStart()启动实时任务调度之前运行
void OSInit (void) { OSInitHookBegin(); /* Call port specific initialization code */ OS_InitMisc(); /* Initialize miscellaneous variables */ OS_InitRdyList(); /* Initialize the Ready List */ OS_InitTCBList(); /* Initialize the free list of OS_TCBs */ OS_InitEventList(); /* Initialize the free list of OS_EVENTs */ #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) OS_FlagInit(); /* Initialize the event flag structures */ #endif #if (OS_MEM_EN > 0u) && (OS_MAX_MEM_PART > 0u) OS_MemInit(); /* Initialize the memory manager */ #endif #if (OS_Q_EN > 0u) && (OS_MAX_QS > 0u) OS_QInit(); /* Initialize the message queue structures */ #endif OS_InitTaskIdle(); /* Create the Idle Task */ #if OS_TASK_STAT_EN > 0u OS_InitTaskStat(); /* Create the Statistic Task */ #endif /*...*/ }
实现对操作系统一些混杂的全局变量的初始化
static void OS_InitMisc (void) { #if OS_TIME_GET_SET_EN > 0u OSTime = 0uL; /* Clear the 32-bit system clock */ #endif OSIntNesting = 0u; /* Clear the interrupt nesting counter */ OSLockNesting = 0u; /* Clear the scheduling lock counter */ OSTaskCtr = 0u; /* Clear the number of tasks */ OSRunning = OS_FALSE; /* Indicate that multitasking not started */ OSCtxSwCtr = 0u; /* Clear the context switch counter */ OSIdleCtr = 0uL; /* Clear the 32-bit idle counter */ #if OS_TASK_STAT_EN > 0u OSIdleCtrRun = 0uL; OSIdleCtrMax = 0uL; OSStatRdy = OS_FALSE; /* Statistic task is not ready */ #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 OSSafetyCriticalStartFlag = OS_FALSE; /* Still allow creation of objects */ #endif }
对就绪表进行初始化的工作
static void OS_InitRdyList (void) { INT8U i; OSRdyGrp = 0u; /* Clear the ready list */ for (i = 0u; i < OS_RDY_TBL_SIZE; i++) { OSRdyTbl[i] = 0u; } OSPrioCur = 0u; OSPrioHighRdy = 0u; OSTCBHighRdy = (OS_TCB *)0; OSTCBCur = (OS_TCB *)0; }
控制块链表初始化
static void OS_InitTCBList (void) { INT8U ix; INT8U ix_next; OS_TCB *ptcb1; OS_TCB *ptcb2; OS_MemClr((INT8U *)&OSTCBTbl[0], sizeof(OSTCBTbl)); /* Clear all the TCBs */ OS_MemClr((INT8U *)&OSTCBPrioTbl[0], sizeof(OSTCBPrioTbl)); /* Clear the priority table */ for (ix = 0u; ix < (OS_MAX_TASKS + OS_N_SYS_TASKS - 1u); ix++) { /* Init. list of free TCBs */ ix_next = ix + 1u; ptcb1 = &OSTCBTbl[ix]; ptcb2 = &OSTCBTbl[ix_next]; ptcb1->OSTCBNext = ptcb2; #if OS_TASK_NAME_EN > 0u ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */ #endif } ptcb1 = &OSTCBTbl[ix]; ptcb1->OSTCBNext = (OS_TCB *)0; /* Last OS_TCB */ #if OS_TASK_NAME_EN > 0u ptcb1->OSTCBTaskName = (INT8U *)(void *)"?"; /* Unknown name */ #endif OSTCBList = (OS_TCB *)0; /* TCB lists initializations */ OSTCBFreeList = &OSTCBTbl[0]; }
创建操作系统空闲任务
static void OS_InitTaskIdle (void) { #if OS_TASK_NAME_EN > 0u INT8U err; #endif #if OS_TASK_CREATE_EXT_EN > 0u #if OS_STK_GROWTH == 1u (void)OSTaskCreateExt(OS_TaskIdle, (void *)0, /* No arguments passed to OS_TaskIdle() */ &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Top-Of-Stack */ OS_TASK_IDLE_PRIO, /* Lowest priority level */ OS_TASK_IDLE_ID, &OSTaskIdleStk[0], /* Set Bottom-Of-Stack */ OS_TASK_IDLE_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */ #else (void)OSTaskCreateExt(OS_TaskIdle, (void *)0, /* No arguments passed to OS_TaskIdle() */ &OSTaskIdleStk[0], /* Set Top-Of-Stack */ OS_TASK_IDLE_PRIO, /* Lowest priority level */ OS_TASK_IDLE_ID, &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u],/* Set Bottom-Of-Stack */ OS_TASK_IDLE_STK_SIZE, (void *)0, /* No TCB extension */ OS_TASK_OPT_STK_CHK | OS_TASK_OPT_STK_CLR);/* Enable stack checking + clear stack */ #endif #else #if OS_STK_GROWTH == 1u (void)OSTaskCreate(OS_TaskIdle, (void *)0, &OSTaskIdleStk[OS_TASK_IDLE_STK_SIZE - 1u], OS_TASK_IDLE_PRIO); #else (void)OSTaskCreate(OS_TaskIdle, (void *)0, &OSTaskIdleStk[0], OS_TASK_IDLE_PRIO); #endif #endif #if OS_TASK_NAME_EN > 0u OSTaskNameSet(OS_TASK_IDLE_PRIO, (INT8U *)(void *)"uC/OS-II Idle", &err); #endif }
任务创建函数分两种,一种是基本的创建函数OSTaskCreate,另一种是扩展的任务创建函数OSTaskCreateExt。
创建任务OS_TaskCreat
INT8U OSTaskCreate (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio) { OS_STK *psp; INT8U err; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 if (OSSafetyCriticalStartFlag == OS_TRUE) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */ return (OS_ERR_PRIO_INVALID); } #endif OS_ENTER_CRITICAL(); if (OSIntNesting > 0u) { /* Make sure we don't create the task from within an ISR */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_CREATE_ISR); } if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */ OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */ /* ... the same thing until task is created. */ OS_EXIT_CRITICAL(); psp = OSTaskStkInit(task, p_arg, ptos, 0u); /* Initialize the task's stack */ err = OS_TCBInit(prio, psp, (OS_STK *)0, 0u, 0u, (void *)0, 0u); if (err == OS_ERR_NONE) { if (OSRunning == OS_TRUE) { /* Find highest priority task if multitasking has started */ OS_Sched(); } } else { OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */ OS_EXIT_CRITICAL(); } return (err); } OS_EXIT_CRITICAL(); return (OS_ERR_PRIO_EXIST); }
堆栈初始化函数OSTaskStkInit的一个版本
OS_STK *OSTaskStkInit (void (*task)(void *pd), void *pdata, OS_STK *ptos, INT16U opt) { INT32U *stk; //console �¼Ĵ���Ϊ32λ�� opt = opt; /* 'opt' is not used, prevent warning */ stk = (INT32U *)ptos; /* Load stack pointer */ *--stk = (INT32U)pdata; /* Simulate call to function with argument */ *--stk = (INT32U)0X00000000; *--stk = (INT32U)task; /* Put pointer to task on top of stack */ *--stk = (INT32U)0x00000202; /* EFL = 0X00000202 */ *--stk = (INT32U)0xAAAAAAAA; /* EAX = 0xAAAAAAAA */ *--stk = (INT32U)0xCCCCCCCC; /* ECX = 0xCCCCCCCC */ *--stk = (INT32U)0xDDDDDDDD; /* EDX = 0xDDDDDDDD */ *--stk = (INT32U)0xBBBBBBBB; /* EBX = 0xBBBBBBBB */ *--stk = (INT32U)0x00000000; /* ESP = 0x00000000 esp�������⣬��Ϊ */ *--stk = (INT32U)0x11111111; /* EBP = 0x11111111 */ *--stk = (INT32U)0x22222222; /* ESI = 0x22222222 */ *--stk = (INT32U)0x33333333; /* EDI = 0x33333333 */ return ((OS_STK *)stk); }
OS_TaskCreat的流程 :在编译环境的初始化 (虚拟机初始化),OS初始化,之后进行,创建完任务后,OSStart()
创建任务OS_TaskCreatExt
与OS_TaskCreat主要的区别就是堆栈的清理
INT8U OSTaskCreateExt (void (*task)(void *p_arg), void *p_arg, OS_STK *ptos, INT8U prio, INT16U id, OS_STK *pbos, INT32U stk_size, void *pext, INT16U opt) { OS_STK *psp; INT8U err; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 if (OSSafetyCriticalStartFlag == OS_TRUE) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (prio > OS_LOWEST_PRIO) { /* Make sure priority is within allowable range */ return (OS_ERR_PRIO_INVALID); } #endif OS_ENTER_CRITICAL(); if (OSIntNesting > 0u) { /* Make sure we don't create the task from within an ISR */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_CREATE_ISR); } if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */ OSTCBPrioTbl[prio] = OS_TCB_RESERVED;/* Reserve the priority to prevent others from doing ... */ /* ... the same thing until task is created. */ OS_EXIT_CRITICAL(); #if (OS_TASK_STAT_STK_CHK_EN > 0u) OS_TaskStkClr(pbos, stk_size, opt); /* Clear the task stack (if needed) */ #endif psp = OSTaskStkInit(task, p_arg, ptos, opt); /* Initialize the task's stack */ err = OS_TCBInit(prio, psp, pbos, id, stk_size, pext, opt); if (err == OS_ERR_NONE) { if (OSRunning == OS_TRUE) { /* Find HPT if multitasking has started */ OS_Sched(); } } else { OS_ENTER_CRITICAL(); OSTCBPrioTbl[prio] = (OS_TCB *)0; /* Make this priority avail. to others */ OS_EXIT_CRITICAL(); } return (err); } OS_EXIT_CRITICAL(); return (OS_ERR_PRIO_EXIST); }
OS_TaskCreatExt的流程
删除任务是创建任务的逆过程,
任务创建设置就绪表, 就绪组, 任务删除则取消设置;
任务创建将任务控制块从空闲链表移到就绪链表; 删除操作则相反。
INT8U OSTaskDel (INT8U prio) { #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) OS_FLAG_NODE *pnode; #endif OS_TCB *ptcb; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (OSIntNesting > 0u) { /* See if trying to delete from ISR */ return (OS_ERR_TASK_DEL_ISR); } if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */ return (OS_ERR_TASK_DEL_IDLE); } #if OS_ARG_CHK_EN > 0u if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ if (prio != OS_PRIO_SELF) { return (OS_ERR_PRIO_INVALID); } } #endif /*$PAGE*/ OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { /* See if requesting to delete self */ prio = OSTCBCur->OSTCBPrio; /* Set priority to delete to current */ } ptcb = OSTCBPrioTbl[prio]; if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); } if (ptcb == OS_TCB_RESERVED) { /* Must not be assigned to Mutex */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_DEL); } OSRdyTbl[ptcb->OSTCBY] &= (OS_PRIO)~ptcb->OSTCBBitX; if (OSRdyTbl[ptcb->OSTCBY] == 0u) { /* Make task not ready */ OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY; } #if (OS_EVENT_EN) if (ptcb->OSTCBEventPtr != (OS_EVENT *)0) { OS_EventTaskRemove(ptcb, ptcb->OSTCBEventPtr); /* Remove this task from any event wait list */ } #if (OS_EVENT_MULTI_EN > 0u) if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from any events' wait lists*/ OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr); } #endif #endif #if (OS_FLAG_EN > 0u) && (OS_MAX_FLAGS > 0u) pnode = ptcb->OSTCBFlagNode; if (pnode != (OS_FLAG_NODE *)0) { /* If task is waiting on event flag */ OS_FlagUnlink(pnode); /* Remove from wait list */ } #endif ptcb->OSTCBDly = 0u; /* Prevent OSTimeTick() from updating */ ptcb->OSTCBStat = OS_STAT_RDY; /* Prevent task from being resumed */ ptcb->OSTCBStatPend = OS_STAT_PEND_OK; if (OSLockNesting < 255u) { /* Make sure we don't context switch */ OSLockNesting++; } OS_EXIT_CRITICAL(); /* Enabling INT. ignores next instruc. */ OS_Dummy(); /* ... Dummy ensures that INTs will be */ OS_ENTER_CRITICAL(); /* ... disabled HERE! */ if (OSLockNesting > 0u) { /* Remove context switch lock */ OSLockNesting--; } OSTaskDelHook(ptcb); /* Call user defined hook */ OSTaskCtr--; /* One less task being managed */ OSTCBPrioTbl[prio] = (OS_TCB *)0; /* Clear old priority entry */ if (ptcb->OSTCBPrev == (OS_TCB *)0) { /* Remove from TCB chain */ ptcb->OSTCBNext->OSTCBPrev = (OS_TCB *)0; OSTCBList = ptcb->OSTCBNext; } else { ptcb->OSTCBPrev->OSTCBNext = ptcb->OSTCBNext; ptcb->OSTCBNext->OSTCBPrev = ptcb->OSTCBPrev; } ptcb->OSTCBNext = OSTCBFreeList; /* Return TCB to free TCB list */ OSTCBFreeList = ptcb; #if OS_TASK_NAME_EN > 0u ptcb->OSTCBTaskName = (INT8U *)(void *)"?"; #endif OS_EXIT_CRITICAL(); if (OSRunning == OS_TRUE) { OS_Sched(); /* Find new highest priority task */ } return (OS_ERR_NONE); } #endif
OS_Dummy();
中断一定的响应时间,用于把任务控制块还回空闲链表里,把相应的就绪组和就续表去掉就绪状态。
通知对方任务, 告诉它要删除你了, 请任务自己删除自己是一种更好的做法。 因为这么做, 任务可以在删除自己之前先放弃自己使用的资源, 如缓冲区、 信号量、 邮箱、 队列等。
OsTaskDelReq名称虽然是请求, 却是集请求和响应于一段代码的。 该代码的功能是:
INT8U OSTaskDelReq (INT8U prio) { INT8U stat; OS_TCB *ptcb; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to delete idle task */ return (OS_ERR_TASK_DEL_IDLE); } #if OS_ARG_CHK_EN > 0u if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ if (prio != OS_PRIO_SELF) { return (OS_ERR_PRIO_INVALID); } } #endif if (prio == OS_PRIO_SELF) { /* See if a task is requesting to ... */ OS_ENTER_CRITICAL(); /* ... this task to delete itself */ stat = OSTCBCur->OSTCBDelReq; /* Return request status to caller */ OS_EXIT_CRITICAL(); return (stat); } OS_ENTER_CRITICAL(); ptcb = OSTCBPrioTbl[prio]; if (ptcb == (OS_TCB *)0) { /* Task to delete must exist */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); /* Task must already be deleted */ } if (ptcb == OS_TCB_RESERVED) { /* Must NOT be assigned to a Mutex */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_DEL); } ptcb->OSTCBDelReq = OS_ERR_TASK_DEL_REQ; /* Set flag indicating task to be DEL. */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); } #endif
OSTaskSuspend将任务阻塞, 也就是被剥夺CPU的使用权而暂时终止运行, 转到阻塞状态。 通过OSTaskSuspend将任务转到阻塞态被称为挂起任务。被挂起的任务不能运行, 直到其他任务以该任务的优先级作为参数调用OSTaskResume来恢复它, 才能将该任务的状态重新设置为就绪状态。
suspend为阻塞态,挂起态通过中断到达
#if OS_TASK_SUSPEND_EN > 0u INT8U OSTaskSuspend (INT8U prio) { BOOLEAN self; OS_TCB *ptcb; INT8U y; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #if OS_ARG_CHK_EN > 0u if (prio == OS_TASK_IDLE_PRIO) { /* Not allowed to suspend idle task */ return (OS_ERR_TASK_SUSPEND_IDLE); } if (prio >= OS_LOWEST_PRIO) { /* Task priority valid ? */ if (prio != OS_PRIO_SELF) { return (OS_ERR_PRIO_INVALID); } } #endif OS_ENTER_CRITICAL(); if (prio == OS_PRIO_SELF) { /* See if suspend SELF */ prio = OSTCBCur->OSTCBPrio; self = OS_TRUE; } else if (prio == OSTCBCur->OSTCBPrio) { /* See if suspending self */ self = OS_TRUE; } else { self = OS_FALSE; /* No suspending another task */ } ptcb = OSTCBPrioTbl[prio]; if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_SUSPEND_PRIO); } if (ptcb == OS_TCB_RESERVED) { /* See if assigned to Mutex */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); } y = ptcb->OSTCBY; OSRdyTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* Make task not ready */ if (OSRdyTbl[y] == 0u) { OSRdyGrp &= (OS_PRIO)~ptcb->OSTCBBitY; } ptcb->OSTCBStat |= OS_STAT_SUSPEND; /* Status of task is 'SUSPENDED' */ OS_EXIT_CRITICAL(); if (self == OS_TRUE) { /* Context switch only if SELF */ OS_Sched(); /* Find new highest priority task */ } return (OS_ERR_NONE); } #endif
有一个检查是否·正在·挂起自己这一步
然后清除就续表相关位,注意如果一个就绪组对应的所有就续表都清零的话,就绪组相关位也要清除,有一个判断的过程。
INT8U OSTaskResume (INT8U prio) { OS_TCB *ptcb; #if OS_CRITICAL_METHOD == 3u /* Storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #if OS_ARG_CHK_EN > 0u if (prio >= OS_LOWEST_PRIO) { /* Make sure task priority is valid */ return (OS_ERR_PRIO_INVALID); } #endif OS_ENTER_CRITICAL(); ptcb = OSTCBPrioTbl[prio]; if (ptcb == (OS_TCB *)0) { /* Task to suspend must exist */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_RESUME_PRIO); } if (ptcb == OS_TCB_RESERVED) { /* See if assigned to Mutex */ OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); } if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) != OS_STAT_RDY) { /* Task must be suspended */ ptcb->OSTCBStat &= (INT8U)~(INT8U)OS_STAT_SUSPEND; /* Remove suspension */ if (ptcb->OSTCBStat == OS_STAT_RDY) { /* See if task is now ready */ if (ptcb->OSTCBDly == 0u) { OSRdyGrp |= ptcb->OSTCBBitY; /* Yes, Make task ready to run */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OS_EXIT_CRITICAL(); if (OSRunning == OS_TRUE) { OS_Sched(); /* Find new highest priority task */ } } else { OS_EXIT_CRITICAL(); } } else { /* Must be pending on event */ OS_EXIT_CRITICAL(); } return (OS_ERR_NONE); } OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_SUSPENDED); }
取消挂起标志,记得如果任务正常,而且没有延时,就要进入就绪了。
任务级调度执行的功能是如果正在运行的任务不是最高优先级任务或者即将被阻塞, 需要选择一个优先级最高的就绪任务运行
OS_Sched中首先判断是不是具有任务调度的条件即:
void OS_Sched (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif OS_ENTER_CRITICAL(); if (OSIntNesting == 0u) { /* Schedule only if all ISRs done and ... */ if (OSLockNesting == 0u) { /* ... scheduler is not locked */ OS_SchedNew(); OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ #if OS_TASK_PROFILE_EN > 0u OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */ #endif OSCtxSwCtr++; /* Increment context switch counter */ OS_TASK_SW(); /* Perform a context switch */ } } } OS_EXIT_CRITICAL(); }
static void OS_SchedNew (void) { INT8U y; y = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)((y << 3u) + OSUnMapTbl[OSRdyTbl[y]]); }
在OS_Task_SW中, 首先将CPU寄存器中的内容压入被换出的任务堆栈中, 然后将被换入的任务的堆栈内容放入到CPU寄存器, 在调度结束恢复时, 从控制器中取出上次保存的任务的地址, 完成了一次任务级调度。
#define OS_TASK_SW() OSCtxSw()
void OSCtxSw(void) { _asm{ lea eax, nextstart ;任务切换回来后从nextstart开始 push eax pushfd ;标志寄存器的值 pushad ;保存EAX -- EDI mov ebx, [OSTCBCur] mov [ebx], esp ;把堆栈入口的地址保存到当前TCB结构中 } OSTaskSwHook(); OSTCBCur = OSTCBHighRdy; OSPrioCur = OSPrioHighRdy; _asm{ mov ebx, [OSTCBCur] mov esp, [ebx] ;得到OSTCBHighRdy的esp popad ;恢复所有通用寄存器,共8个 popfd ;恢复标志寄存器 ret ;跳转到指定任务运行 } nextstart: //任务切换回来的运行地址 return; }
如果正在运行的任务没有关闭中断,在中断到来的时候, 操作系统响应中断, 进入中断服务程序。这时候任务的运行环境还没有保存, 因此需要将任务的运行环境保存。这时候任务由于中断的到来而进入挂起态。
中断进入
void OSIntEnter (void) { if (OSRunning == OS_TRUE) { if (OSIntNesting < 255u) { OSIntNesting++; /* Increment ISR nesting level */ } } }
中断退出
void OSIntExit (void) { #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif if (OSRunning == OS_TRUE) { OS_ENTER_CRITICAL(); if (OSIntNesting > 0u) { /* Prevent OSIntNesting from wrapping */ OSIntNesting--; } if (OSIntNesting == 0u) { /* Reschedule only if all ISRs complete ... */ if (OSLockNesting == 0u) { /* ... and not locked. */ OS_SchedNew(); OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ #if OS_TASK_PROFILE_EN > 0u OSTCBHighRdy->OSTCBCtxSwCtr++; /* Inc. # of context switches to this task */ #endif OSCtxSwCtr++; /* Keep track of the number of ctx switches */ OSIntCtxSw(); /* Perform interrupt level ctx switch */ } } } OS_EXIT_CRITICAL(); } }
通过OSTimeTick()调用中断
void OSTimeTick (void) { OS_TCB *ptcb; BOOLEAN step; OSTimeTickHook(); /*调用用户钩子函数,默认是空函数 */ #if OS_TIME_GET_SET_EN > 0u OS_ENTER_CRITICAL(); /* Update the 32-bit tick counter */ OSTime++; OS_EXIT_CRITICAL(); #endif if (OSRunning == OS_TRUE) { #if OS_TICK_STEP_EN > 0u switch (OSTickStepState) { /* Determine whether we need to process a tick */ case OS_TICK_STEP_DIS: /* Yes, stepping is disabled */ step = OS_TRUE; break; case OS_TICK_STEP_WAIT: /* No, waiting for uC/OS-View to set ... */ step = OS_FALSE; /* .. OSTickStepState to OS_TICK_STEP_ONCE */ break; case OS_TICK_STEP_ONCE: /* Yes, process tick once and wait for next ... */ step = OS_TRUE; /* ... step command from uC/OS-View */ OSTickStepState = OS_TICK_STEP_WAIT; break; default: /* Invalid case, correct situation */ step = OS_TRUE; OSTickStepState = OS_TICK_STEP_DIS; break; } if (step == OS_FALSE) { /* Return if waiting for step command */ return; } #endif ptcb = OSTCBList; /* Point at first TCB in TCB list */ while (ptcb->OSTCBPrio != OS_TASK_IDLE_PRIO) { /* Go through all TCBs in TCB list */ OS_ENTER_CRITICAL(); if (ptcb->OSTCBDly != 0u) { /* No, Delayed or waiting for event with TO */ ptcb->OSTCBDly--; /* Decrement nbr of ticks to end of delay */ if (ptcb->OSTCBDly == 0u) { /* Check for timeout */ if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { ptcb->OSTCBStat &= (INT8U)~(INT8U)OS_STAT_PEND_ANY; /* Yes, Clear status flag */ ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout */ } else { ptcb->OSTCBStatPend = OS_STAT_PEND_OK; } if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */ OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; } } } ptcb = ptcb->OSTCBNext; /* Point at next TCB in TCB list */ OS_EXIT_CRITICAL(); } } }
在OSTimeTick中, 遍历每一个任务的ptcb->OSTCBDly是否设置了时间延时或者时间等待延时,如果延时时间不为0, 则将延时时间片-1, 表明经过了一个时间片,如果时间片为0, 检查任务是在等待某一事件还是单纯地延时,如果任务不是被挂起, 则延时时间到, 更新就绪表, 将任务状态设置为就绪状态, 循环进行
多任务启动的代码是内核中的OSStart()函数, 在OSStart之前, 必须已经执行了系统初始化函数OSInit, 并且至少创建了1个以上的任务
其实就是把最高优先级任务复制到当前任务,然后启动当前任务
void OSStart (void) { if (OSRunning == OS_FALSE) { OS_SchedNew(); /* Find highest priority's task priority number */ OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ } }
void OSStartHighRdy(void) { OSTaskSwHook(); OSRunning = TRUE; _asm{ mov ebx, [OSTCBCur] ;OSTCBCur结构的第一个参数就是esp mov esp, [ebx] ;恢复堆栈 popad ;恢复所有通用寄存器,共8个 popfd ;恢复标志寄存器 ret ;ret 指令相当于pop eip 但保护模式下不容许使用eip ;永远都不返回 } }
空闲任务是μC/OS-II 的系统任务, 因为它占据了最低优先级63, 所以只有在其他的任务都因为等待事件的发生而被阻塞的时候才能得到运行
void OS_TaskIdle (void *p_arg) { p_arg = p_arg; /* Prevent compiler warning for not using 'p_arg' */ for (;;) { OS_ENTER_CRITICAL(); OSIdleCtr++; OS_EXIT_CRITICAL(); OSTaskIdleHook(); /* Call user definable HOOK */ } }
统计任务OS_TaskStat是μC/OS-II的另一个重要的系统任务, 我们可以通过宏设置取消统计任务, 但一般情况下我们不会这么做, 因为统计任务执行的统计工作是比较重要的。 统计任务的主要功能就是计算CPU的利用率。 如果没有统计任务, 我们就不可知道多任务环境下系统的运行情况是否良好
涉及到的全局变量
#if OS_TASK_STAT_EN > 0u OS_EXT INT8U OSCPUUsage; /* Percentage of CPU used */ OS_EXT INT32U OSIdleCtrMax; /* Max. value that idle ctr can take in 1 sec. */ OS_EXT INT32U OSIdleCtrRun; /* Val. reached by idle ctr at run time in 1 sec. */ OS_EXT BOOLEAN OSStatRdy; /* Flag indicating that the statistic task is rdy */ OS_EXT OS_STK OSTaskStatStk[OS_TASK_STAT_STK_SIZE]; /* Statistics task stack */ #endif
初始化函数OSStatInit
void OSStatInit (void) { OSTimeDly(2u); /* Synchronize with clock tick */ OS_ENTER_CRITICAL(); OSIdleCtr = 0uL; /* Clear idle counter */ OS_EXIT_CRITICAL(); OSTimeDly(OS_TICKS_PER_SEC / 10u); /* Determine MAX. idle counter value for 1/10 second */ OS_ENTER_CRITICAL(); OSIdleCtrMax = OSIdleCtr; /* Store maximum idle counter count in 1/10 second */ //printf("空闲计数最大=%d",OSIdleCtrMax); OSStatRdy = OS_TRUE; OS_EXIT_CRITICAL(); }
OS_TaskStat
void OS_TaskStat (void *p_arg) { p_arg = p_arg; /* Prevent compiler warning for not using 'p_arg' */ while (OSStatRdy == OS_FALSE) { OSTimeDly(2u * OS_TICKS_PER_SEC / 10u); /* Wait until statistic task is ready */ } OSIdleCtrMax /= 100uL; if (OSIdleCtrMax == 0uL) { OSCPUUsage = 0u; #if OS_TASK_SUSPEND_EN > 0u (void)OSTaskSuspend(OS_PRIO_SELF); #else for (;;) { OSTimeDly(OS_TICKS_PER_SEC); } #endif } for (;;) { OS_ENTER_CRITICAL(); OSIdleCtrRun = OSIdleCtr; /* Obtain the of the idle counter for the past second */ OSIdleCtr = 0uL; /* Reset the idle counter for the next second */ OS_EXIT_CRITICAL(); OSCPUUsage = (INT8U)(100uL - OSIdleCtrRun / OSIdleCtrMax); OSTaskStatHook(); /* Invoke user definable hook */ #if (OS_TASK_STAT_STK_CHK_EN > 0u) && (OS_TASK_CREATE_EXT_EN > 0u) OS_TaskStatStkChk(); /* Check the stacks for each task */ #endif OSTimeDly(OS_TICKS_PER_SEC / 10u); /* Accumulate OSIdleCtr for the next 1/10 second */ } }
同上
如果正在运行的任务没有关闭中断,在中断到来的时候, 操作系统响应中断,进入中断服务程序。这时候任务的运行环境还没有保存, 因此需要将任务的运行环境保存。这时候任务由于中断的到来而进入挂起态。
时钟中断服务是要依赖于中断的, 如果是单片机系统,那么就该设置为定时器中断。 用定时器中断的服务程序来完成该功能是恰当的。 对于Windows平台下的虚拟系统, 可以采用定时器触发来虚拟中断。
时间管理的内容在代码os_time.c中, 包含了操作系统时间的设置及获取,对任务的延时, 任务按分秒延时, 取消任务的延时共5个系统调用。
volatile INT32U OSTime;
任务控制块 TCB中的OSTCBDly
注意: 因为OSTime 是被保护的全局变量, 在访问的时候必须使用OS_ENTER_CRITICAL()进入临界区, 保证独占访问!
OSTimeGet
INT32U OSTimeGet (void) { INT32U ticks; OS_ENTER_CRITICAL(); ticks = OSTime; OS_EXIT_CRITICAL(); return (ticks); } #endif
OSTimeSet
void OSTimeSet (INT32U ticks) { OS_ENTER_CRITICAL(); OSTime = ticks; OS_EXIT_CRITICAL(); }
任务延时函数OSTimeDly用于阻塞任务一定时间, 这个时间以参数的形式给出。 如果这个参数的值是N, 那么在N个时间片(时钟嘀嗒) 之后, 任务才能回到就绪状态获得继续运行的机会。 如果参数的值是0, 不会阻塞任务。
void OSTimeDly (INT32U ticks) { INT8U y; if (OSIntNesting > 0u) { /* See if trying to call from an ISR */ return; } if (OSLockNesting > 0u) { /* See if called with scheduler locked */ return; } if (ticks > 0u) { /* 0 means no delay! */ OS_ENTER_CRITICAL(); y = OSTCBCur->OSTCBY; /* Delay current task */ OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX; if (OSRdyTbl[y] == 0u) { OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY; } OSTCBCur->OSTCBDly = ticks; /* Load ticks in TCB */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next task to run! */ } }
调用OSTimeDly(1) 会阻塞 <=1个时间片
OSTimeDlyHMSM从功能上来说和OSTimeDly并没有多大的差别,只是将时间单位进行了转换, 也就是说转换了以小时、 分、 秒、 毫秒为单位的时间和以时间片为单位的时间。 OSTimeDlyHMSM的参数分别是延时的小时数hours, 分钟数minutes, 秒数seconds, 和毫秒数ms
INT8U OSTimeDlyHMSM (INT8U hours, INT8U minutes, INT8U seconds, INT16U ms) { INT32U ticks; if (OSIntNesting > 0u) { /* See if trying to call from an ISR */ return (OS_ERR_TIME_DLY_ISR); } if (OSLockNesting > 0u) { /* See if called with scheduler locked */ return (OS_ERR_SCHED_LOCKED); } #if OS_ARG_CHK_EN > 0u if (hours == 0u) { if (minutes == 0u) { if (seconds == 0u) { if (ms == 0u) { return (OS_ERR_TIME_ZERO_DLY); } } } } if (minutes > 59u) { return (OS_ERR_TIME_INVALID_MINUTES); /* Validate arguments to be within range */ } if (seconds > 59u) { return (OS_ERR_TIME_INVALID_SECONDS); } if (ms > 999u) { return (OS_ERR_TIME_INVALID_MS); } #endif /* Compute the total number of clock ticks required.. */ /* .. (rounded to the nearest tick) */ ticks = ((INT32U)hours * 3600uL + (INT32U)minutes * 60uL + (INT32U)seconds) * OS_TICKS_PER_SEC + OS_TICKS_PER_SEC * ((INT32U)ms + 500uL / OS_TICKS_PER_SEC) / 1000uL; OSTimeDly(ticks); return (OS_ERR_NONE); }
阻塞状态的任务即便任务的延时时间没到, 还是可以通过OSTimeDlyResume恢复该任务到就绪态。 对于因等待事件发生而阻塞的, 且设置了超时timeout时间的任务, 也可以时候OSTimeDlyResume来恢复。 对这些任务使用了OSTimeDlyResume, 就好像已经等待超时了一样! 但是, 采用OSTaskSuspend挂起的任务, 是不允许采用OSTimeDlyResume来恢复。
INT8U OSTimeDlyResume (INT8U prio) { OS_TCB *ptcb; if (prio >= OS_LOWEST_PRIO) { return (OS_ERR_PRIO_INVALID); } OS_ENTER_CRITICAL(); ptcb = OSTCBPrioTbl[prio]; /* Make sure that task exist */ if (ptcb == (OS_TCB *)0) { OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); /* The task does not exist */ } if (ptcb == OS_TCB_RESERVED) { OS_EXIT_CRITICAL(); return (OS_ERR_TASK_NOT_EXIST); /* The task does not exist */ } if (ptcb->OSTCBDly == 0u) { /* See if task is delayed */ OS_EXIT_CRITICAL(); return (OS_ERR_TIME_NOT_DLY); /* Indicate that task was not delayed */ } ptcb->OSTCBDly = 0u; /* Clear the time delay */ if ((ptcb->OSTCBStat & OS_STAT_PEND_ANY) != OS_STAT_RDY) { ptcb->OSTCBStat &= ~OS_STAT_PEND_ANY; /* Yes, Clear status flag */ ptcb->OSTCBStatPend = OS_STAT_PEND_TO; /* Indicate PEND timeout */ } else { ptcb->OSTCBStatPend = OS_STAT_PEND_OK; } if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { /* Is task suspended? */ OSRdyGrp |= ptcb->OSTCBBitY; /* No, Make ready */ OSRdyTbl[ptcb->OSTCBY] |= ptcb->OSTCBBitX; OS_EXIT_CRITICAL(); OS_Sched(); /* See if this is new highest priority */ } else { OS_EXIT_CRITICAL(); /* Task may be suspended */ } return (OS_ERR_NONE); }
μC/OS-II作为实时多任务操作系统, 是事件驱动的, 支持信号量, 消息等机制。
事件主要包括信号量和互斥信号量, 而事件的组合可以用事件标志组来管理 。
事件控制块ECB在事件管理中占据着举足轻重的作用。 虽然事件控制块ECB并没有任务控制块TCB的内容丰富, 但是在事件处理中仍然是核心的数据结构, 频繁被访问。
typedef struct os_event { INT8U OSEventType; /* Type of event control block (see OS_EVENT_TYPE_xxxx) */ void *OSEventPtr; /* Pointer to message or queue structure */ INT16U OSEventCnt; /* Semaphore Count (not used if other EVENT type) */ OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur */ OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */ } OS_EVENT;
OS_EVENT_TYPE
#define OS_EVENT_TYPE_UNUSED 0u #define OS_EVENT_TYPE_MBOX 1u #define OS_EVENT_TYPE_Q 2u #define OS_EVENT_TYPE_SEM 3u #define OS_EVENT_TYPE_MUTEX 4u #define OS_EVENT_TYPE_FLAG 5u
当事件发生的时候, 操作系统会找到优先级最高的等待事件发生的任务,并将该任务就绪, 然后在事件等待组和事件等待表中取消该任务的标记。
事件管理中, 将空闲的事件块链接为一个单向的链表, 事件控制块空闲链表。这个链表的形式和任务块TCB的空闲链表的形式是完全相同的。
当创建一个事件的时候, 要在事件控制块ECB空闲链表查找是否有空闲的ECB可用。 如果有, 就从链表中取出分配给事件
事件空闲链表指针OSEventFreeList的定义为:
OS_EVENT *OSEventFreeList;
static void OS_InitEventList (void) { #if (OS_EVENT_EN) && (OS_MAX_EVENTS > 0u) #if (OS_MAX_EVENTS > 1u) INT16U ix; INT16U ix_next; OS_EVENT *pevent1; OS_EVENT *pevent2; OS_MemClr((INT8U *)&OSEventTbl[0], sizeof(OSEventTbl)); /* Clear the event table */ for (ix = 0u; ix < (OS_MAX_EVENTS - 1u); ix++) { /* Init. list of free EVENT control blocks */ ix_next = ix + 1u; pevent1 = &OSEventTbl[ix]; pevent2 = &OSEventTbl[ix_next]; pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; pevent1->OSEventPtr = pevent2; #if OS_EVENT_NAME_EN > 0u pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */ #endif } pevent1 = &OSEventTbl[ix]; pevent1->OSEventType = OS_EVENT_TYPE_UNUSED; pevent1->OSEventPtr = (OS_EVENT *)0; #if OS_EVENT_NAME_EN > 0u pevent1->OSEventName = (INT8U *)(void *)"?"; /* Unknown name */ #endif OSEventFreeList = &OSEventTbl[0]; #else OSEventFreeList = &OSEventTbl[0]; /* Only have ONE event control block */ OSEventFreeList->OSEventType = OS_EVENT_TYPE_UNUSED; OSEventFreeList->OSEventPtr = (OS_EVENT *)0; #if OS_EVENT_NAME_EN > 0u OSEventFreeList->OSEventName = (INT8U *)"?"; /* Unknown name */ #endif #endif #endif }
当建立一个事件或消息, 如信号量、 邮箱、 消息队列时, 如信号量的建立函数
OSSemCreate等, 需要对事件等待表进行初始化。
OS_EventWaitListInit
pevent->OSEventGrp = 0u; /* 清空任务等待组*/ for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { pevent->OSEventTbl[i] = 0u; /* 采用循环模式清空任务等待表*/ }
当任务等待事件发生, 并获得事件控制块ECB后, 需要在ECB中标记任务在等待事件的发生, 才可以在事件发生时取消任务的阻塞
void OS_EventTaskWait (OS_EVENT *pevent) { INT8U y; OSTCBCur->OSTCBEventPtr = pevent; /* Store ptr to ECB in TCB */ pevent->OSEventTbl[OSTCBCur->OSTCBY] |= OSTCBCur->OSTCBBitX; /* Put task in waiting list */ pevent->OSEventGrp |= OSTCBCur->OSTCBBitY; y = OSTCBCur->OSTCBY; /* Task no longer ready */ OSRdyTbl[y] &= (OS_PRIO)~OSTCBCur->OSTCBBitX; if (OSRdyTbl[y] == 0u) { /* Clear event grp bit if this was only task pending */ OSRdyGrp &= (OS_PRIO)~OSTCBCur->OSTCBBitY; } }
OS_EventTaskRemove 是与OS_EventTaskWait相反的操作, 当一个任务由于某种原因不再需要等待事件需运行OS_EventTaskRemove 。
void OS_EventTaskRemove (OS_TCB *ptcb, OS_EVENT *pevent) { INT8U y; y = ptcb->OSTCBY; pevent->OSEventTbl[y] &= (OS_PRIO)~ptcb->OSTCBBitX; /* 在事件等待表中删除事件等待标志 */ if (pevent->OSEventTbl[y] == 0u) { /*若该行已没有任务等待*/ pevent->OSEventGrp &= (OS_PRIO)~ptcb->OSTCBBitY; /*删除事件等待组的事件等待标志*/ } }
任务因为等待事件而在ECB中登记自己的等待, 当事件发生的时候, 如果该任务是事件等待表中优先级最高的任务, 任务被取消等待而回到就绪状态。
OS_EventTaskRdy
INT8U OS_EventTaskRdy (OS_EVENT *pevent, void *pmsg, INT8U msk, INT8U pend_stat) { OS_TCB *ptcb; INT8U y; INT8U x; INT8U prio; #if OS_LOWEST_PRIO > 63u OS_PRIO *ptbl; #endif #if OS_LOWEST_PRIO <= 63u y = OSUnMapTbl[pevent->OSEventGrp]; /* Find HPT waiting for message */ x = OSUnMapTbl[pevent->OSEventTbl[y]]; prio = (INT8U)((y << 3u) + x); /* Find priority of task getting the msg */ #else if ((pevent->OSEventGrp & 0xFFu) != 0u) { /* Find HPT waiting for message */ y = OSUnMapTbl[ pevent->OSEventGrp & 0xFFu]; } else { y = OSUnMapTbl[(OS_PRIO)(pevent->OSEventGrp >> 8u) & 0xFFu] + 8u; } ptbl = &pevent->OSEventTbl[y]; if ((*ptbl & 0xFFu) != 0u) { x = OSUnMapTbl[*ptbl & 0xFFu]; } else { x = OSUnMapTbl[(OS_PRIO)(*ptbl >> 8u) & 0xFFu] + 8u; } prio = (INT8U)((y << 4u) + x); /* Find priority of task getting the msg */ #endif ptcb = OSTCBPrioTbl[prio]; /* Point to this task's OS_TCB */ ptcb->OSTCBDly = 0u; /* Prevent OSTimeTick() from readying task */ #if ((OS_Q_EN > 0u) && (OS_MAX_QS > 0u)) || (OS_MBOX_EN > 0u) ptcb->OSTCBMsg = pmsg; /* Send message directly to waiting task */ #else pmsg = pmsg; /* Prevent compiler warning if not used */ #endif ptcb->OSTCBStat &= (INT8U)~msk; /* Clear bit associated with event type */ ptcb->OSTCBStatPend = pend_stat; /* Set pend status of post or abort */ /* See if task is ready (could be susp'd) */ if ((ptcb->OSTCBStat & OS_STAT_SUSPEND) == OS_STAT_RDY) { OSRdyGrp |= ptcb->OSTCBBitY; /* Put task in the ready to run list */ OSRdyTbl[y] |= ptcb->OSTCBBitX; } OS_EventTaskRemove(ptcb, pevent); /* Remove this task from event wait list */ #if (OS_EVENT_MULTI_EN > 0u) if (ptcb->OSTCBEventMultiPtr != (OS_EVENT **)0) { /* Remove this task from events' wait lists */ OS_EventTaskRemoveMulti(ptcb, ptcb->OSTCBEventMultiPtr); ptcb->OSTCBEventPtr = (OS_EVENT *)pevent;/* Return event as first multi-pend event ready*/ } #endif return (prio); }
信号量在操作系统初始化的时候并不存在。 这时操作系统中的事件管理数据结构事件控制块ECB为全空, 所有的事件控制块都在ECB空闲链表中排队。 信号量的建立函数OSSemCreate将使用一个并配置一个ECB, 使其具备信号量的属性。
OS_EVENT *OSSemCreate (INT16U cnt) { OS_EVENT *pevent; if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ return ((OS_EVENT *)0); /* ... can't CREATE from an ISR */ } OS_ENTER_CRITICAL(); pevent = OSEventFreeList; /* Get next free event control block */ if (OSEventFreeList != (OS_EVENT *)0) { /* See if pool of free ECB pool was empty */ OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { /* Get an event control block */ pevent->OSEventType = OS_EVENT_TYPE_SEM; pevent->OSEventCnt = cnt; /* Set semaphore value */ pevent->OSEventPtr = (void *)0; /* Unlink from ECB free list */ #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif OS_EventWaitListInit(pevent); /* Initialize to 'nobody waiting' on sem. */ } return (pevent); }
假设信号量值为5, 则赋值后的ECB应该如图4-4所示。
信号量如果不再使用了就应该尽快删除, 否则很快系统就没有可用的事件块可用。信号量的删除函数是OSSemDel。 删除信号量比创建一个信号量更复杂 。
OS_EVENT *OSSemDel (OS_EVENT *pevent, INT8U opt, INT8U *perr) { BOOLEAN tasks_waiting; OS_EVENT *pevent_return; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return (pevent); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ *perr = OS_ERR_EVENT_TYPE; return (pevent); } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */ return (pevent); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any tasks waiting on semaphore */ tasks_waiting = OS_TRUE; /* Yes */ } else { tasks_waiting = OS_FALSE; /* No */ } switch (opt) { case OS_DEL_NO_PEND: /* Delete semaphore only if no task waiting */ if (tasks_waiting == OS_FALSE) { #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* Get next free event control block */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */ } else { OS_EXIT_CRITICAL(); *perr = OS_ERR_TASK_WAITING; pevent_return = pevent; } break; case OS_DEL_ALWAYS: /* Always delete the semaphore */ while (pevent->OSEventGrp != 0u) { /* Ready ALL tasks waiting for semaphore */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK); } #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* Get next free event control block */ OS_EXIT_CRITICAL(); if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */ OS_Sched(); /* Find highest priority task ready to run */ } *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Semaphore has been deleted */ break; default: OS_EXIT_CRITICAL(); *perr = OS_ERR_INVALID_OPT; pevent_return = pevent; break; } return (pevent_return); }
请求信号量也称为等待信号量。 等待信号量的参数为3个, 分别是ECB的指针pevent, 32位无符号整数超时时间timeout, 和用来返回结果的指向整型的指针perr
void OSSemPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr) { #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return; } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ *perr = OS_ERR_EVENT_TYPE; return; } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */ return; } if (OSLockNesting > 0u) { /* See if called with scheduler locked ... */ *perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */ return; } OS_ENTER_CRITICAL(); if (pevent->OSEventCnt > 0u) { /* If sem. is positive, resource available ... */ pevent->OSEventCnt--; /* ... decrement semaphore only if positive. */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return; } /* Otherwise, must wait until event occurs */ OSTCBCur->OSTCBStat |= OS_STAT_SEM; /* Resource not available, pend on semaphore */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Store pend timeout in TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); *perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */ break; } OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */ OS_EXIT_CRITICAL(); }
OS_EventTaskWait(pevent);
作用就是标记与取消标记,在事件等待表中进行标记,在就续表、组进行取消标记。
当任务A获得信号量之后将信号量数字减1, 然后就可以访问资源R。 这时, 如果信号量的值为0, 任务B如果也要访问资源R, 必须等待信号量, 因此将任务B阻塞。 任务A在对资源的访问完成之后, 应将信号量唤醒。 因为资源已经可以被其他的任务访问了, 因此应该将任务B唤醒, 使任务B就绪。
当访问资源的任务有2个以上, 资源R可同时被N个任务访问, 因此信号量的值在最开始创建的时候应该等于N。 当任务A访问信号量, 信号量值变为N-1,任务B又访问, 信号量等于N-2, 当第M个任务访问, 信号量等于N-M。 当N-M=0的时候,也就是当N=M的时候, 当第N+1也要访问该资源R,第N+1个任务必须等待。
当任何一个任务(例如第2个)访问资源完成, 应该唤醒第N+1个任务让其访问资源。 当第N+1个任务访问完成之后, 因为没有其他的任务等待信号量, 只需简单地将信号量值加 1
INT8U OSSemPost (OS_EVENT *pevent) { #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ return (OS_ERR_PEVENT_NULL); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any task waiting for semaphore */ /* Ready HPT waiting on event */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find HPT ready to run */ return (OS_ERR_NONE); } if (pevent->OSEventCnt < 65535u) { /* Make sure semaphore will not overflow */ pevent->OSEventCnt++; /* Increment semaphore count to register event */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); } OS_EXIT_CRITICAL(); /* Semaphore value has reached its maximum */ return (OS_ERR_SEM_OVF); }
在中断服务程序和有些用户任务中, 需要无等待的请求信号量。 也就是说, 使用信号量请求资源, 当没有可用的资源, 信号量为0的时候, 并不阻塞自己, 而是继续执行其他代码。 OSSemAccept就是无等待的请求信号量函数 。
INT16U OSSemAccept (OS_EVENT *pevent) { INT16U cnt; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ return (0u); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ return (0u); } OS_ENTER_CRITICAL(); cnt = pevent->OSEventCnt; if (cnt > 0u) { /* See if resource is available */ pevent->OSEventCnt--; /* Yes, decrement semaphore and notify caller */ } OS_EXIT_CRITICAL(); return (cnt); /* Return semaphore count */ }
放弃等待信号量并非放弃本任务对信号量的等待。如果是放弃本任务对信号量的等待, 那么本任务在等待信号量, 那么本任务应该处于阻塞状态, 一个处于阻塞状态的任务得不到运行, 怎么能执行放弃等待信号量的代码呢? 因此, 一定是放弃其他任务对一个信号量的等待。
INT8U OSSemPendAbort (OS_EVENT *pevent, INT8U opt, INT8U *perr) { INT8U nbr_tasks; #ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return (0u); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ *perr = OS_ERR_EVENT_TYPE; return (0u); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any task waiting on semaphore? */ nbr_tasks = 0u; switch (opt) { case OS_PEND_OPT_BROADCAST: /* Do we need to abort ALL waiting tasks? */ while (pevent->OSEventGrp != 0u) { /* Yes, ready ALL tasks waiting on semaphore */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT); nbr_tasks++; } break; case OS_PEND_OPT_NONE: default: /* No, ready HPT waiting on semaphore */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_SEM, OS_STAT_PEND_ABORT); nbr_tasks++; break; } OS_EXIT_CRITICAL(); OS_Sched(); /* Find HPT ready to run */ *perr = OS_ERR_PEND_ABORT; return (nbr_tasks); } OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (0u); /* No tasks waiting on semaphore */ }
操作系统提供了直接设置信号量值的函数OSSemSet。 一般情况下无需使用该函数设置信号量的值, 应该在信号量创建的时候初始化信号量的值。当一个信号量的值在创建之后为N, 每次有任务请求信号量就将该值减1, 反之将该值加1, 一般情况下是不允许随便修改的。
但是, 在极其特殊的情况下, 因为某种特殊的需要, 例如突然增加了其他的资源,需要修改资源数N, 可采用OSSemSet直接对信号量赋值, 但条件是这时没有任务在等待该信号量。
void OSSemSet (OS_EVENT *pevent, INT16U cnt, INT8U *perr) { #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return; } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ *perr = OS_ERR_EVENT_TYPE; return; } OS_ENTER_CRITICAL(); *perr = OS_ERR_NONE; if (pevent->OSEventCnt > 0u) { /* See if semaphore already has a count */ pevent->OSEventCnt = cnt; /* Yes, set it to the new value specified. */ } else { /* No */ if (pevent->OSEventGrp == 0u) { /* See if task(s) waiting? */ pevent->OSEventCnt = cnt; /* No, OK to set the value */ } else { *perr = OS_ERR_TASK_WAITING; } } OS_EXIT_CRITICAL(); }
信号量状态查询将ECB中关于信号量的信息拷贝到另一个数据结构信号量数据OS_SEM_DATA
OS_SEM_DATA
typedef struct os_sem_data { INT16U OSCnt; /* Semaphore count */ OS_PRIO OSEventTbl[OS_EVENT_TBL_SIZE]; /* List of tasks waiting for event to occur */ OS_PRIO OSEventGrp; /* Group corresponding to tasks waiting for event to occur */ } OS_SEM_DATA;
OSSemQuery
INT8U OSSemQuery (OS_EVENT *pevent, OS_SEM_DATA *p_sem_data) { INT8U i; OS_PRIO *psrc; OS_PRIO *pdest; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* 事件控制指针是否有效 */ return (OS_ERR_PEVENT_NULL); } if (p_sem_data == (OS_SEM_DATA *)0) { /* Validate 'p_sem_data' */ return (OS_ERR_PDATA_NULL); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_SEM) { /* 事件控制块类型是否有效 */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); p_sem_data->OSEventGrp = pevent->OSEventGrp; /* Copy message mailbox wait list */ psrc = &pevent->OSEventTbl[0]; pdest = &p_sem_data->OSEventTbl[0]; for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } p_sem_data->OSCnt = pevent->OSEventCnt; /* Get semaphore count */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); }
调度\(\sigma\)
任务集\(\varGamma\)
算法A
可行性feasible
可调度性shedulable
约束constraints
NP hard
可抢占/不可抢占
静态/动态
在线/离线
最佳的/直觉的
\(\alpha \;| \;\beta \;|\; \gamma\)
\(\alpha\) 处理器数 number of processors
\(\beta\) 任务约束 constraints on tasks
\(\gamma\) 遵循优化准则optimality criterion
响应时间取决到达时间
静态
不适合实时
不适合实时
会产生饥饿,可以通过aging解决
时间片轮转
不适合实时
不适合实时
静态:相对截止时间Di
动态:绝对截止时间di
Li = fi - di 延迟时间 = 截止时间 - 结束时间
1 | sync | Lmax
同时到达,非抢占,静态
1 | preem. | Lmax
抢占式,非同时到达,动态
如果一个任务集不能被一个最优算法调度,那么这个任务集不能被其他算法调度
非抢占的EDF在不能空闲的算法里是最佳的
1 | no-preem | Lmax
对调度树进行修剪,找到Lmax最小化
改为找直觉函数H最小化
H = ri => FCFS
H = Ci => SJF
H = Di => DM
H = di => EDF
Composite heuristic functions:
H =w1ri + w2Di
H = w1Ci+ w2di
H = w1ri + w2di
通过直觉函数有可能找不到最优,但并不代表不存在最优,有可能是是被前序直觉计算时忽略了
1 | prec, sync | Lmax
先计算截止时间最晚的,放在最后执行
1 | prec, preem | Lmax
将先后次序改为时间约束,然后用EDF调度
先将后续任务的开始时间延后,再将前序任务的截止时间提前
Ui = required feeding fraction
\(\Delta\) = GCD(T1, T2)
\(\delta _i = U_i\Delta\)
可行性测试feasibility test:\(\Sigma\delta_i \le \Delta\) i.e. \(\Sigma U_i \le 1\)
有可能GCD公约数取得太小,导致分割的块太多,会有很多转换的开销,增加运行的时间
大周期里有小周期
不需要实时系统内核,开销比较小
过载后不是很鲁棒,不容易扩展
Pi 正比 1/Ti (static)
在所有固定优先级算法中T=D(周期=相对截止时间)时是最佳的
Pi 正比 1/Di (static)
在所有固定优先级算法中D<=T时是最佳的
Pi 正比 1/dik (dynamic)
di,k = ri,k + Di
在所有算法中是最佳的
高优先级任务到来时刻
Up <= 1 是可行性调度的必要不充分条件
Up <= Ulub 是RM调度的充分条件
并非必要条件,因为在Ulub到1之间,可能存在一部分可行,比如Ulub < A <1 在Ulub到A之间可行,A到1之间不可行。
RM的上界Ulub : \(U_{lub}^{Rm} = n(2^{\frac{1}{n}} - 1)\)
\(\prod_{i=1}^{n}(U_i + 1) \le 2\)
\(I_i = \sum_{D_k < D_i}C_k\)
响应时间Ri = Ci + Ii
验证Ri <= Di
\(I_ik = \lceil \frac{R_i}{T_k} \rceil C_k\)
\(I_i = \sum_{k=1}^{i-1} \lceil \frac{R_i}{T_k} \rceil C_k\)
因为EDF跟截止时间相关,是动态的,所以不能用响应时间分析。
processor demand criterion
\(\forall L > 0, \; g(0,L) \le L\)
\(g(0,L) = \sum_{i=1}^n \lfloor \frac{L-D_i+T_i}{T_i}\rfloor C_i\)
含义:gi(o,L) = 计算次数(时长/周期) * 计算时间,g(o,L) =
-Di+Ti为了保证整数运算,效果就是产生溢出,再向下取整
bounding test points
由于g函数只会在截止时间到来产生跳变,所以只要测试边界点就可以了
如果任务同时到达,而且Up < 1,经过超周期后,任务都是重复出现的,最小的超周期为H = lcm(T1,...,Tn)
\(G(0,L) = \sum_{i=1}^n(\frac{L+T_i-D_i}{T_i})C_i = LU + \sum_{i=1}^n(T_i - D_i)U_i\)
从图像上可以看出来,我们只需要计算到L*
综合以上,得到处理器需求测试(Processor Demand Test)
\(\forall L \in D, \; g(0,L) \le L\)
\(D = {d_k | d_k \le \min(H,L^*)}\)
本章的消息管理中包括消息邮箱和消息队列两方面的内容, 适用于任务之间的信息交流和同步。从原理上讲, 消息管理也应该属于事件管理的范畴
本章的消息管理中包括消息邮箱和消息队列两方面的内容, 适用于任务之间的信息交流和同步。从原理上讲, 消息管理也应该属于事件管理的范畴
在系统初始化之后, 并不存在一个消息邮箱。 这时操作系统中的事件管理数据结构事件控制块ECB为全空, 所有的事件控制块都在ECB空闲链表中排队。 消息邮箱的建立函数OSMboxCreate将使用一个并配置一个ECB,使其具备消息邮箱的属性。
OS_EVENT *OSMboxCreate (void *pmsg) { OS_EVENT *pevent; if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数*/ return ((OS_EVENT *)0); /* 不允许在中断服务程序ISR中调用本函数*/ } OS_ENTER_CRITICAL(); pevent = OSEventFreeList; /* 取得空闲的事件控制块ECB*/ if (OSEventFreeList != (OS_EVENT *)0) { /* 是否没有可用的事件控制块ECB? */ OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { pevent->OSEventType = OS_EVENT_TYPE_MBOX; pevent->OSEventCnt = 0u; pevent->OSEventPtr = pmsg; /* 在ECB中存储消息地址*/ #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif OS_EventWaitListInit(pevent); } return (pevent); /* 返回事件控制块地址*/ }
等消息也称为请求消息。 含义是当消息存在的时候获取消息, 当消息不存在的时候就放弃对CPU的占有, 直到有消息的时候才被唤醒。 当任务后续的操作离不开消息, 这时任务就不该死死占着CPU不让其他的任务运行, 就应该去休息,而当消息到来的时候系统会将消息唤醒回就绪态, 任务获得消息后继续运行。
void *OSMboxPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr) { void *pmsg; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return ((void *)0); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效 */ *perr = OS_ERR_EVENT_TYPE; return ((void *)0); } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */ return ((void *)0); } if (OSLockNesting > 0u) { /* See if called with scheduler locked ... */ *perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */ return ((void *)0); } OS_ENTER_CRITICAL(); pmsg = pevent->OSEventPtr; if (pmsg != (void *)0) { /* See if there is already a message */ pevent->OSEventPtr = (void *)0; /* 清邮箱 */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (pmsg); /* 返回获得的消息(或空指针) */ } OSTCBCur->OSTCBStat |= OS_STAT_MBOX; /* Message not available, task will pend */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Load timeout in TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready to run */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: pmsg = OSTCBCur->OSTCBMsg; *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: pmsg = (void *)0; *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); pmsg = (void *)0; *perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */ break; } OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */ #if (OS_EVENT_MULTI_EN > 0u) OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0; #endif OSTCBCur->OSTCBMsg = (void *)0; /* 清除获得的消息 */ OS_EXIT_CRITICAL(); return (pmsg); /* 返回获得的消息 */ }
当一个任务因为等待消息而被阻塞的时候, 只有当其他任务发出了消息, 被阻塞的任务才能被恢复到就绪态, 从而获得消息后继续运行。 阻塞的函数在前一节分析过了, 发消息的函数为OSMboxPost, 参数是消息类型的ECB的指针,以及消息的地址
INT8U OSMboxPost (OS_EVENT *pevent, void *pmsg) { #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ return (OS_ERR_PEVENT_NULL); } if (pmsg == (void *)0) { /* Make sure we are not posting a NULL pointer */ return (OS_ERR_POST_NULL_PTR); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效 */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any task pending on mailbox */ /* Ready HPT waiting on event */ (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_MBOX, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find highest priority task ready to run */ return (OS_ERR_NONE); } if (pevent->OSEventPtr != (void *)0) { /* Make sure mailbox doesn't already have a msg */ OS_EXIT_CRITICAL(); return (OS_ERR_MBOX_FULL); } pevent->OSEventPtr = pmsg; /* Place message in mailbox */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); }
当消息邮箱不再使用了, 就应该尽快归还给系统, 即将消息占用的ECB归还给ECB空闲链表以备它用。 消息邮箱的删除函数是OSMboxDel。 删除一个消息也要涉及方方面面, 因为可能有任务正在等待这个邮箱中的消息。
#if OS_MBOX_DEL_EN > 0u OS_EVENT *OSMboxDel (OS_EVENT *pevent, INT8U opt, INT8U *perr) { BOOLEAN tasks_waiting; OS_EVENT *pevent_return; #ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return (pevent); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效*/ *perr = OS_ERR_EVENT_TYPE; return (pevent); } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */ return (pevent); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any tasks waiting on mailbox */ tasks_waiting = OS_TRUE; /* Yes */ } else { tasks_waiting = OS_FALSE; /* No */ } switch (opt) { case OS_DEL_NO_PEND: /* Delete mailbox only if no task waiting */ if (tasks_waiting == OS_FALSE) { #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* 取得空闲的事件控制块ECB */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Mailbox has been deleted */ } else { OS_EXIT_CRITICAL(); *perr = OS_ERR_TASK_WAITING; pevent_return = pevent; } break; case OS_DEL_ALWAYS: /* Always delete the mailbox */ while (pevent->OSEventGrp != 0u) { /* Ready ALL tasks waiting for mailbox */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_OK); } #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* 取得空闲的事件控制块ECB */ OS_EXIT_CRITICAL(); if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */ OS_Sched(); /* Find highest priority task ready to run */ } *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Mailbox has been deleted */ break; default: OS_EXIT_CRITICAL(); *perr = OS_ERR_INVALID_OPT; pevent_return = pevent; break; } return (pevent_return); }
同放弃对信号量的等待类似, 放弃等待邮箱也绝对不会是放弃本任务对邮箱的等待。放弃等待邮箱函数将放弃的是所有等待某邮箱的任务对该邮箱的等待或等待某邮箱的优先级最高的任务对邮箱的等待
INT8U OSMboxPendAbort (OS_EVENT *pevent, INT8U opt, INT8U *perr) { INT8U nbr_tasks; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ *perr = OS_ERR_PEVENT_NULL; return (0u); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效*/ *perr = OS_ERR_EVENT_TYPE; return (0u); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any task waiting on mailbox? */ nbr_tasks = 0u; switch (opt) { case OS_PEND_OPT_BROADCAST: /* Do we need to abort ALL waiting tasks? */ while (pevent->OSEventGrp != 0u) { /* Yes, ready ALL tasks waiting on mailbox */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_ABORT); nbr_tasks++; } break; case OS_PEND_OPT_NONE: default: /* No, ready HPT waiting on mailbox */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_MBOX, OS_STAT_PEND_ABORT); nbr_tasks++; break; } OS_EXIT_CRITICAL(); OS_Sched(); /* Find HPT ready to run */ *perr = OS_ERR_PEND_ABORT; return (nbr_tasks); } OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (0u); /* No tasks waiting on mailbox */ }
检查事件控制块指针是否有效及事件控制块类型是否有效。
如果pevent->OSEventGrp为0说明没有任务等待消息邮箱, 取消等待的任务数是0, 返回0。
否则根据参数opt(选项)进行分支转移,如为OS_PEND_OPT_BROADCAST, 使用while语句循环地将等待该邮箱的每个任务用OS_EventTaskRdy来取消等待并使其就绪( 除非任务还被挂起) ;如果为其他值则只将最高优先级的任务取消等待并就绪之。
返回取消等待信号量的任务数。
在中断服务程序和有些用户任务中, 需要无等待的请求消息邮箱。 也就是说, 到邮箱中取邮件, 如果有邮件就获得邮件, 如果没有并不阻塞自己, 而是继续执行其他代码。
void *OSMboxAccept (OS_EVENT *pevent) { void *pmsg; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ return ((void *)0); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效 */ return ((void *)0); } OS_ENTER_CRITICAL(); pmsg = pevent->OSEventPtr; pevent->OSEventPtr = (void *)0; /* 清邮箱 */ OS_EXIT_CRITICAL(); return (pmsg); /* 返回获得的消息(或空指针) */ }
首先参数检查ECB是否有效, 如果有效, 将消息邮箱中邮件的地址OSEventPtr赋值给pmsg, 然后清邮箱内容, 返回获得的邮件的地址pmsg。这样, 如果邮箱中有邮件, 那么返回邮件的地址, 如果没有, 返回值就是空地址。
INT8U OSMboxQuery (OS_EVENT *pevent, OS_MBOX_DATA *p_mbox_data) { INT8U i; OS_PRIO *psrc; OS_PRIO *pdest; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /*事件控制块指针是否有效 */ return (OS_ERR_PEVENT_NULL); } if (p_mbox_data == (OS_MBOX_DATA *)0) { /* Validate 'p_mbox_data' */ return (OS_ERR_PDATA_NULL); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_MBOX) { /* 事件控制块类型是否有效*/ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); p_mbox_data->OSEventGrp = pevent->OSEventGrp; /* Copy message mailbox wait list */ psrc = &pevent->OSEventTbl[0]; pdest = &p_mbox_data->OSEventTbl[0]; for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } p_mbox_data->OSMsg = pevent->OSEventPtr; /* Get message from mailbox */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); }
使用消息队列管理, 就允许使用可以容纳多条信息的大邮箱, 按照先进先出( FIFO) 的原则, 发送和接收邮件。 需要注意的是, 这样的邮箱不是操作系统提供的, 而是要由用户任务来提供。 操作系统提供的是对其进行管理的程序。 另外, 邮箱中的内容仍然是邮件的地址。
void *MessageStorage[size]; typedef struct os_q { /* 队列控制块QCB*/ struct os_q *OSQPtr; /*在空闲QCB链表中, 指示下一个QCB*/ void **OSQStart; /*队列数据的首地址*/ void **OSQEnd; /*队列数据的末地址+1*/ void **OSQIn; /*指示下次插入消息的位置 */ void **OSQOut; /* 指示下次提取消息的位置*/ INT16U OSQSize; /*队列的最大容量*/ INT16U OSQEntries; /*队列中当前的消息量*/ } OS_Q;
OS_Q OSQTbl[OS_MAX_QS];
消息队列初始化函数在操作系统初始化时被调用, 主要用于初始化消息队列使用的数据结构。 消息队列初始化函数的名称为OS_QInit
void OS_QInit (void) { #if OS_MAX_QS == 1u OSQFreeList = &OSQTbl[0]; /* Only ONE queue! */ OSQFreeList->OSQPtr = (OS_Q *)0; #endif #if OS_MAX_QS >= 2u INT16U ix; INT16U ix_next; OS_Q *pq1; OS_Q *pq2; OS_MemClr((INT8U *)&OSQTbl[0], sizeof(OSQTbl)); /* Clear the queue table */ for (ix = 0u; ix < (OS_MAX_QS - 1u); ix++) { /* Init. list of free QUEUE control blocks */ ix_next = ix + 1u; pq1 = &OSQTbl[ix]; pq2 = &OSQTbl[ix_next]; pq1->OSQPtr = pq2; } pq1 = &OSQTbl[ix]; pq1->OSQPtr = (OS_Q *)0; OSQFreeList = &OSQTbl[0]; #endif }
创建消息队列就是将从ECB空闲链表中取下一个事件控制块ECB来, 将其用于消息队列管理。 并从QCB空闲链表的表头取下一个消息控制块QCB, 将其各种属性进行设置, 用于指示消息的位置以及提取和插入消息的位置。创建消息队列的函数名称为OSQCreate。
OS_EVENT *OSQCreate (void **start, INT16U size) { OS_EVENT *pevent; OS_Q *pq; #if OS_CRITICAL_METHOD == 3u /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr = 0u; #endif #ifdef OS_SAFETY_CRITICAL_IEC61508 if (OSSafetyCriticalStartFlag == OS_TRUE) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ return ((OS_EVENT *)0); /* 不允许在中断服务程序ISR中调用本函数 */ } OS_ENTER_CRITICAL(); pevent = OSEventFreeList; /* 取得事件控制块ECB */ if (OSEventFreeList != (OS_EVENT *)0) { /*是否有有效的ECB */ OSEventFreeList = (OS_EVENT *)OSEventFreeList->OSEventPtr; } OS_EXIT_CRITICAL(); if (pevent != (OS_EVENT *)0) { /* 是否有有效的ECB */ OS_ENTER_CRITICAL(); pq = OSQFreeList; /* 在空闲QCB链表中取一个QCB */ if (pq != (OS_Q *)0) { /* 得到了有效的QCB吗? */ OSQFreeList = OSQFreeList->OSQPtr; /* 取下该QCB*/ OS_EXIT_CRITICAL(); pq->OSQStart = start; /* 队列初始化 */ pq->OSQEnd = &start[size]; pq->OSQIn = start; pq->OSQOut = start; pq->OSQSize = size; pq->OSQEntries = 0u; pevent->OSEventType = OS_EVENT_TYPE_Q; pevent->OSEventCnt = 0u; pevent->OSEventPtr = pq; #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif OS_EventWaitListInit(pevent); /* 初始化事件等待表 */ } else { pevent->OSEventPtr = (void *)OSEventFreeList; OSEventFreeList = pevent; OS_EXIT_CRITICAL(); pevent = (OS_EVENT *)0; } } return (pevent); }
发消息到消息队列的函数名称为OSQPost。 参数是事件控制块ECB的地址pevent和消息的地址pmsg。
INT8U OSQPost (OS_EVENT *pevent, void *pmsg) { OS_Q *pq; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */ return (OS_ERR_PEVENT_NULL); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any task pending on queue */ /* Ready highest priority task waiting on event */ (void)OS_EventTaskRdy(pevent, pmsg, OS_STAT_Q, OS_STAT_PEND_OK); OS_EXIT_CRITICAL(); OS_Sched(); /* Find highest priority task ready to run */ return (OS_ERR_NONE); } pq = (OS_Q *)pevent->OSEventPtr; /* Point to queue control block */ if (pq->OSQEntries >= pq->OSQSize) { /* Make sure queue is not full */ OS_EXIT_CRITICAL(); return (OS_ERR_Q_FULL); } *pq->OSQIn++ = pmsg; /* Insert message into queue */ pq->OSQEntries++; /* Update the nbr of entries in the queue */ if (pq->OSQIn == pq->OSQEnd) { /* Wrap IN ptr if we are at end of queue */ pq->OSQIn = pq->OSQStart; } OS_EXIT_CRITICAL(); return (OS_ERR_NONE); }
等待消息队列的消息是消息队列管理中的又一核心函数。 如果消息队列中有消息,那么就取出消息, 然后返回; 如果没有消息, 只有在ECB中标记自己的等待, 然后阻塞。
等待消息队列的函数的名称为OSQPend, 参数是ECB的指针、 等待超时时间和返回函数执行信息的指针的perr。 函数的返回值是指向消息的指针。
void *OSQPend (OS_EVENT *pevent, INT32U timeout, INT8U *perr) { void *pmsg; OS_Q *pq; #ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */ *perr = OS_ERR_PEVENT_NULL; return ((void *)0); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_Q) {/* Validate event block type */ *perr = OS_ERR_EVENT_TYPE; return ((void *)0); } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_PEND_ISR; /* ... can't PEND from an ISR */ return ((void *)0); } if (OSLockNesting > 0u) { /* See if called with scheduler locked ... */ *perr = OS_ERR_PEND_LOCKED; /* ... can't PEND when locked */ return ((void *)0); } OS_ENTER_CRITICAL(); pq = (OS_Q *)pevent->OSEventPtr; /* Point at queue control block */ if (pq->OSQEntries > 0u) { /* See if any messages in the queue */ pmsg = *pq->OSQOut++; /* Yes, extract oldest message from the queue */ pq->OSQEntries--; /* Update the number of entries in the queue */ if (pq->OSQOut == pq->OSQEnd) { /* Wrap OUT pointer if we are at the end of the queue */ pq->OSQOut = pq->OSQStart; } OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; return (pmsg); /* Return message received */ } OSTCBCur->OSTCBStat |= OS_STAT_Q; /* Task will have to pend for a message to be posted */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; OSTCBCur->OSTCBDly = timeout; /* Load timeout into TCB */ OS_EventTaskWait(pevent); /* Suspend task until event or timeout occurs */ OS_EXIT_CRITICAL(); OS_Sched(); /* Find next highest priority task ready to run */ OS_ENTER_CRITICAL(); switch (OSTCBCur->OSTCBStatPend) { /* See if we timed-out or aborted */ case OS_STAT_PEND_OK: /* Extract message from TCB (Put there by QPost) */ pmsg = OSTCBCur->OSTCBMsg; *perr = OS_ERR_NONE; break; case OS_STAT_PEND_ABORT: pmsg = (void *)0; *perr = OS_ERR_PEND_ABORT; /* Indicate that we aborted */ break; case OS_STAT_PEND_TO: default: OS_EventTaskRemove(OSTCBCur, pevent); pmsg = (void *)0; *perr = OS_ERR_TIMEOUT; /* Indicate that we didn't get event within TO */ break; } OSTCBCur->OSTCBStat = OS_STAT_RDY; /* Set task status to ready */ OSTCBCur->OSTCBStatPend = OS_STAT_PEND_OK; /* Clear pend status */ OSTCBCur->OSTCBEventPtr = (OS_EVENT *)0; /* Clear event pointers */ #if (OS_EVENT_MULTI_EN > 0u) OSTCBCur->OSTCBEventMultiPtr = (OS_EVENT **)0; #endif OSTCBCur->OSTCBMsg = (void *)0; /* Clear received message */ OS_EXIT_CRITICAL(); return (pmsg); /* Return received message */ }
当消息队列不再使用了, 就应该尽快归还给系统, 即将消息占用的ECB归还给ECB空闲链表以备它用, 将QCB也归还给空闲QCB链表。
OS_EVENT *OSQDel (OS_EVENT *pevent, INT8U opt, INT8U *perr) { BOOLEAN tasks_waiting; OS_EVENT *pevent_return; OS_Q *pq; #ifdef OS_SAFETY_CRITICAL if (perr == (INT8U *)0) { OS_SAFETY_CRITICAL_EXCEPTION(); } #endif #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */ *perr = OS_ERR_PEVENT_NULL; return (pevent); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */ *perr = OS_ERR_EVENT_TYPE; return (pevent); } if (OSIntNesting > 0u) { /* 查看是否在中断服务程序ISR中调用本函数 */ *perr = OS_ERR_DEL_ISR; /* ... can't DELETE from an ISR */ return (pevent); } OS_ENTER_CRITICAL(); if (pevent->OSEventGrp != 0u) { /* See if any tasks waiting on queue */ tasks_waiting = OS_TRUE; /* Yes */ } else { tasks_waiting = OS_FALSE; /* No */ } switch (opt) { case OS_DEL_NO_PEND: /* Delete queue only if no task waiting */ if (tasks_waiting == OS_FALSE) { #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pq = (OS_Q *)pevent->OSEventPtr; /* Return OS_Q to free list */ pq->OSQPtr = OSQFreeList; OSQFreeList = pq; pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* 取得事件控制块ECB */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Queue has been deleted */ } else { OS_EXIT_CRITICAL(); *perr = OS_ERR_TASK_WAITING; pevent_return = pevent; } break; case OS_DEL_ALWAYS: /* Always delete the queue */ while (pevent->OSEventGrp != 0u) { /* Ready ALL tasks waiting for queue */ (void)OS_EventTaskRdy(pevent, (void *)0, OS_STAT_Q, OS_STAT_PEND_OK); } #if OS_EVENT_NAME_EN > 0u pevent->OSEventName = (INT8U *)(void *)"?"; #endif pq = (OS_Q *)pevent->OSEventPtr; /* Return OS_Q to free list */ pq->OSQPtr = OSQFreeList; OSQFreeList = pq; pevent->OSEventType = OS_EVENT_TYPE_UNUSED; pevent->OSEventPtr = OSEventFreeList; /* Return Event Control Block to free list */ pevent->OSEventCnt = 0u; OSEventFreeList = pevent; /* 取得事件控制块ECB */ OS_EXIT_CRITICAL(); if (tasks_waiting == OS_TRUE) { /* Reschedule only if task(s) were waiting */ OS_Sched(); /* Find highest priority task ready to run */ } *perr = OS_ERR_NONE; pevent_return = (OS_EVENT *)0; /* Queue has been deleted */ break; default: OS_EXIT_CRITICAL(); *perr = OS_ERR_INVALID_OPT; pevent_return = pevent; break; } return (pevent_return); }
消息队列数据OS_Q_DATA是为返回消息队列信息而提供的, 因此用户程序如果想了解消息队列的信息, 要先创建OS_Q_DATA的实例。 然后以消息队列所在ECB地址及该实例的地址为指针为参数调用获取消息队列的状态函数OSQQuery
INT8U OSQQuery (OS_EVENT *pevent, OS_Q_DATA *p_q_data) { OS_Q *pq; INT8U i; OS_PRIO *psrc; OS_PRIO *pdest; #if OS_ARG_CHK_EN > 0u if (pevent == (OS_EVENT *)0) { /* Validate 'pevent' */ return (OS_ERR_PEVENT_NULL); } if (p_q_data == (OS_Q_DATA *)0) { /* Validate 'p_q_data' */ return (OS_ERR_PDATA_NULL); } #endif if (pevent->OSEventType != OS_EVENT_TYPE_Q) { /* Validate event block type */ return (OS_ERR_EVENT_TYPE); } OS_ENTER_CRITICAL(); p_q_data->OSEventGrp = pevent->OSEventGrp; /* Copy message queue wait list */ psrc = &pevent->OSEventTbl[0]; pdest = &p_q_data->OSEventTbl[0]; for (i = 0u; i < OS_EVENT_TBL_SIZE; i++) { *pdest++ = *psrc++; } pq = (OS_Q *)pevent->OSEventPtr; if (pq->OSQEntries > 0u) { p_q_data->OSMsg = *pq->OSQOut; /* Get next message to return if available */ } else { p_q_data->OSMsg = (void *)0; } p_q_data->OSNMsgs = pq->OSQEntries; p_q_data->OSQSize = pq->OSQSize; OS_EXIT_CRITICAL(); return (OS_ERR_NONE); }
嵌入式系统中, 内存资源是十分宝贵的, 如果采用内存分配方式不合理, 经过一段时间的内存分配和释放、 再发配和再释放, 会产生很多零散的内存碎块。 这些零散的空间很难利用, 因此怎样解决内存分配过程中产生的碎块问题是内存管理的关键问题
μC/OS-II中, 采用分区的方式管理内存, 即将连续的大块内存按分区来管理, 每个系统中有数个这样的分区, 每个分区又包含数个相同的内存块。 这样, 在分配内存的时候,根据需要从不同的分区中得到数个内存块, 而在释放时,这些内存块重新释放回他们原来所在的分区。 这样就不会产生内存越分越凌乱, 没有整块的连续分区的问题了。
typedef struct os_mem { /* MEMORY CONTROL BLOCK */ void *OSMemAddr; /* Pointer to beginning of memory partition */ void *OSMemFreeList; /* Pointer to list of free memory blocks */ INT32U OSMemBlkSize; /* Size (in bytes) of each block of memory */ INT32U OSMemNBlks; /* Total number of blocks in this partition */ INT32U OSMemNFree; /* Number of memory blocks remaining in this partition */ #if OS_MEM_NAME_EN > 0u INT8U *OSMemName; /* Memory partition name */ #endif } OS_MEM;
内存控制块实体
OS_MEM OSMemTbl[OS_MAX_MEM_PART];
空闲内存控制块链表
OS_MEM *OSMemFreeList;
内存分区与消息队列在一点上相似, 就是必须由用户任务来创建。 其实定义一个内存分区是相当简单的一件事情, 其实就是一个二维数组。
如: INT32U MemBuf [10] [20]
MemBuf就可以是一个内存分区, 该分区共有800个字节, 分为10个内存块。使用MCB可以将该分区管理起来, 实现动态分配和释放
void OS_MemInit (void) { #if OS_MAX_MEM_PART == 1u OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl)); /* Clear the memory partition table */ OSMemFreeList = (OS_MEM *)&OSMemTbl[0]; /* Point to beginning of free list */ #if OS_MEM_NAME_EN > 0u OSMemFreeList->OSMemName = (INT8U *)"?"; /* Unknown name */ #endif #endif #if OS_MAX_MEM_PART >= 2u OS_MEM *pmem; INT16U i; OS_MemClr((INT8U *)&OSMemTbl[0], sizeof(OSMemTbl)); /* Clear the memory partition table */ for (i = 0u; i < (OS_MAX_MEM_PART - 1u); i++) { /* Init. list of free memory partitions */ pmem = &OSMemTbl[i]; /* Point to memory control block (MCB) */ pmem->OSMemFreeList = (void *)&OSMemTbl[i + 1u]; /* Chain list of free partitions */ #if OS_MEM_NAME_EN > 0u pmem->OSMemName = (INT8U *)(void *)"?"; #endif } pmem = &OSMemTbl[i]; pmem->OSMemFreeList = (void *)0; /* Initialize last node */ #if OS_MEM_NAME_EN > 0u pmem->OSMemName = (INT8U *)(void *)"?"; #endif OSMemFreeList = &OSMemTbl[0]; /* Point to beginning of free list */ #endif }
OS_MemClr这个函数很简单, 将从pdest开始一个字节一个字节的将内存中的内容清0, 直到清0了size个字节为止
void OS_MemClr (INT8U *pdest, INT16U size) { while (size > 0u) { *pdest++ = (INT8U)0; size--; } }
可见OS_MemInit, 对内存控制块MCB进行了初始化, 构建了空闲MCB链表。但并未执行创建内存分区及分配内存的操作
内存分区在操作系统初始化的时候并不存在。 在使用一个内存分区之前, 必须先定
义一个二维数组, 但这个二维数组仍未成为内存分区。
通过调用函数OSMemCreate, 使用一个MCB对其进行管理, 才成为一个内存分区。
OSMemCreate返回一个指向内存控制块的指针, 供内存管理的其他操作函数调用。
OSMemCreate有四个参数:
OS_MEM *OSMemCreate (void *addr, INT32U nblks, INT32U blksize, INT8U *perr) { OS_MEM *pmem; INT8U *pblk; void **plink; INT32U loops; INT32U i; #if OS_ARG_CHK_EN > 0u if (addr == (void *)0) { /* Must pass a valid address for the memory part.*/ *perr = OS_ERR_MEM_INVALID_ADDR; return ((OS_MEM *)0); } if (((INT32U)addr & (sizeof(void *) - 1u)) != 0u){ /* Must be pointer size aligned */ *perr = OS_ERR_MEM_INVALID_ADDR; return ((OS_MEM *)0); } if (nblks < 2u) { /* Must have at least 2 blocks per partition */ *perr = OS_ERR_MEM_INVALID_BLKS; return ((OS_MEM *)0); } if (blksize < sizeof(void *)) { /* Must contain space for at least a pointer */ *perr = OS_ERR_MEM_INVALID_SIZE; return ((OS_MEM *)0); } #endif OS_ENTER_CRITICAL(); pmem = OSMemFreeList; /* Get next free memory partition */ if (OSMemFreeList != (OS_MEM *)0) { /* See if pool of free partitions was empty */ OSMemFreeList = (OS_MEM *)OSMemFreeList->OSMemFreeList; } OS_EXIT_CRITICAL(); if (pmem == (OS_MEM *)0) { /* See if we have a memory partition */ *perr = OS_ERR_MEM_INVALID_PART; return ((OS_MEM *)0); } plink = (void **)addr; /* Create linked list of free memory blocks */ pblk = (INT8U *)addr; loops = nblks - 1u; for (i = 0u; i < loops; i++) { pblk += blksize; /* Point to the FOLLOWING block */ *plink = (void *)pblk; /* Save pointer to NEXT block in CURRENT block */ plink = (void **)pblk; /* Position to NEXT block */ } *plink = (void *)0; /* Last memory block points to NULL */ pmem->OSMemAddr = addr; /* Store start address of memory partition */ pmem->OSMemFreeList = addr; /* Initialize pointer to pool of free blocks */ pmem->OSMemNFree = nblks; /* Store number of free blocks in MCB */ pmem->OSMemNBlks = nblks; pmem->OSMemBlkSize = blksize; /* Store block size of each memory blocks */ *perr = OS_ERR_NONE; return (pmem); }
首先, 当操作系统需要创建内存分区时, 调用OSMemCreate()函数, 并通过参数传递需要建立内存分区的属性, 包括: 内存分区的首地址, 内存分区中内存块的数量, 内存分区中内存块的大小, 以及返回信息代码的地址。
然后, 检查判断是否执行函数参数检查, 以保证内存分区的创建成功。 如果执行函数参数检查, 则判断: 内存分区地址是否有效, 内存分区地址大小是否一致, 内存分区是否至少含有两个内存块, 每个内存块是否起码可以容纳一个指针。
最后, 执行创建内存分区的算法, 创建一个内存分区, 并返回内存控制块的地址, 供系统调用。 内存分区创建的算法是: 将内存分区首地址加内存块大小得到下一个内存块地址, 然后再加内存块大小得到下一个内存块地址, 如此循环(内存块数量-1) 次, 直至最后一个内存块创建完成。 在其过程中, 通过指针建立内存块的链接表, 并将最后一个内存块内的指针指向空地址。
创建之后, 形成了一个空闲内存块链表, 而该链表由内存控制块MCB来管理。应用程序需要从已建立的内存分区中申请一个内存块时, 可以调用OSMemGet函数。 该函数有两个参数, 第一个参数为pmem, 也就是前节创建内存分区函数所返回的内存控制块的指针。 另一个参数为perr, 用于返回函数执行的信心
void *OSMemGet (OS_MEM *pmem, INT8U *perr) { void *pblk; #if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ *perr = OS_ERR_MEM_INVALID_PMEM; return ((void *)0); } #endif OS_ENTER_CRITICAL(); if (pmem->OSMemNFree > 0u) { /* See if there are any free memory blocks */ pblk = pmem->OSMemFreeList; /* Yes, point to next free memory block */ pmem->OSMemFreeList = *(void **)pblk; /* Adjust pointer to new free list */ pmem->OSMemNFree--; /* One less memory block in this partition */ OS_EXIT_CRITICAL(); *perr = OS_ERR_NONE; /* No error */ return (pblk); /* Return memory block to caller */ } OS_EXIT_CRITICAL(); *perr = OS_ERR_MEM_NO_FREE_BLKS; /* No, Notify caller of empty memory partition */ return ((void *)0); /* Return NULL pointer to caller */ }
内存块的释放, 就是将内存块归还给空闲内存块链表, 也可以理解为归还给内存分区。 内存分区释放函数是OSMemPut。
内存块释放函数是OSMemPut有两个参数, 一个是内存控制块MCB的地址, 一个是将释放的内存块的地址。 OSMemPut返回整数型的操作过程的信息号
INT8U OSMemPut (OS_MEM *pmem, void *pblk) { if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ return (OS_ERR_MEM_INVALID_PMEM); } if (pblk == (void *)0) { /* Must release a valid block */ return (OS_ERR_MEM_INVALID_PBLK); } OS_ENTER_CRITICAL(); if (pmem->OSMemNFree >= pmem->OSMemNBlks) { /* Make sure all blocks not already returned */ OS_EXIT_CRITICAL(); return (OS_ERR_MEM_FULL); } *(void **)pblk = pmem->OSMemFreeList; /* Insert released block into free block list */ pmem->OSMemFreeList = pblk; pmem->OSMemNFree++; /* One more memory block in this partition */ OS_EXIT_CRITICAL(); return (OS_ERR_NONE); /* Notify caller that memory block was released */ }
从本节的OSMemPut和上节OSMemGet,很明显, 实现了内存的动态分配, 并且一次分配和释放最小是一个内存块, 保证了内存中不会存在很多小的碎片。
INT8U OSMemQuery (OS_MEM *pmem, OS_MEM_DATA *p_mem_data) { #if OS_ARG_CHK_EN > 0u if (pmem == (OS_MEM *)0) { /* Must point to a valid memory partition */ return (OS_ERR_MEM_INVALID_PMEM); } if (p_mem_data == (OS_MEM_DATA *)0) { /* Must release a valid storage area for the data */ return (OS_ERR_MEM_INVALID_PDATA); } #endif OS_ENTER_CRITICAL(); p_mem_data->OSAddr = pmem->OSMemAddr; p_mem_data->OSFreeList = pmem->OSMemFreeList; p_mem_data->OSBlkSize = pmem->OSMemBlkSize; p_mem_data->OSNBlks = pmem->OSMemNBlks; p_mem_data->OSNFree = pmem->OSMemNFree; OS_EXIT_CRITICAL(); p_mem_data->OSNUsed = p_mem_data->OSNBlks - p_mem_data->OSNFree; return (OS_ERR_NONE); }