Ch6. uC/OS-II分析 宋健建 南京大学软件学院
内容 uC/OS-II概述 一些基本概念 内核情景分析 完整的程序 源代码文件结构 任务、任务控制块、任务间通信、共享与互斥、中断 从boot到操作系统,操作系统初始化,任务创建,启动多任务,任务调度,任务切换,任务定时等待… 完整的程序
uC/OS-II概述 源代码开放 微内核 内核与应用软件没有明确区分 易移植 共享同一个地址空间,一个映像,一个main()入口函数 与通用操作系统,或其他比较大比较通用的嵌入式操作系统如Linux,Windows CE的比较; 方便由test case改造;
Application Software CPU Timer uC/OS-II Port uC/OS-II (Processor Independent Code) OS_MBOX.C OS_CORE.C OS_MEM.C uCOS_II.C OS_Q.C uCOS_II.H OS_SEM.C OS_TASK.C OS_FLAG.C OS_TIME.C OS_MUTEX.C uC/OS-II Configuration (Application Specific) OS_CFG.H INCLUDE.H uC/OS-II Port (Processor Specific Code) OS_CPU.H OS_CPU_A.A OS_CPU_C.C CPU Timer
uC/OS-II源代码组成 核心部分(OS_Core.c) 任务处理部分(OS_Task.c) 时钟部分(OS_Time.c) 操作系统的处理核心。包括操作系统初始化、操作系统运行、中断进出的前导、时钟节拍、任务调度、事件处理等多部分。能够维持系统基本工作的部分都在这里。 任务处理部分(OS_Task.c) 与任务的操作密切相关,包括任务的建立、删除、挂起、恢复等等。 时钟部分(OS_Time.c) uC/OS-II中的最小时钟单位是timetick(时钟节拍)。任务延时等操作在这里完成的。
uC/OS-II源代码组成(续) 任务同步和通信部分 与CPU的接口部分 事件处理部分,包括信号量、邮箱、邮箱队列、事件标志等部分;主要用于任务间的互相联系和对临界资源的访问。 与CPU的接口部分 uC/OS-II针对所使用的CPU的移植部分。主要包括中断级任务切换的底层实现、任务级任务切换的底层实现、时钟节拍的产生和处理、中断的相关处理部分等内容。由于涉及SP等系统指针,通常用汇编语言编写。
应用范例 一个PC机上的基于uC/OS-II的应用范例 13个任务 空闲任务和统计任务(OSInit产生) TaskStart任务 10个任务在屏幕随机的位置显示0-9的数字,每个任务只显示同一个数字
内容 uC/OS-II概述 一些基本概念 内核情景分析 完整的程序 源代码文件结构 任务、任务控制块、任务间通信、共享与互斥、中断 从boot到操作系统,操作系统初始化,任务创建,启动多任务,任务调度,任务切换,任务定时等待… 完整的程序
任务 实时应用程序的设计过程包括如何把问题分割成多个任务 每个任务 任务与进程/线程 是整个应用的一部分 被赋予一定的优先级 有自己的一套CPU寄存器和栈空间 任务与进程/线程
任务的创建 Interface Example INT8U OSTaskCreate ( void (*task)(void *pd), void *pdata, OS_STK *ptos, INT8U prio); Example OSTaskCreate(TaskStart, (void *)0, &TaskStartStk[TASK_STK_SIZE - 1], 0); OSTaskCreate(Task, (void *)&TaskData[i], &TaskStk[i][TASK_STK_SIZE – 1], i+1);
SetTickIn5MS() IRQ_Handler() OSTaskStkInit() OSStartHighRdy() IntTaskSwitch()
任务控制块 OS_TCB (in uCOS_II.h) typedef struct os_tcb { OS_STK *OSTCBStkPtr; /* Pointer to current top of stack */ #if OS_TASK_CREATE_EXT_EN > 0 … #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_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) || (OS_SEM_EN > 0) || (OS_MUTEX_EN > 0) OS_EVENT *OSTCBEventPtr; /* Pointer to event control block */
任务控制块(续) OS_TCB (in uCOS_II.h) #if ((OS_Q_EN > 0) && (OS_MAX_QS > 0)) || (OS_MBOX_EN > 0) void *OSTCBMsg; /* Message received from OSMboxPost() or OSQPost() */ #endif #if (OS_VERSION >= 251) && (OS_FLAG_EN > 0) && (OS_MAX_FLAGS > 0) #if OS_TASK_DEL_EN > 0 OS_FLAG_NODE *OSTCBFlagNode; /* Pointer to event flag node */ OS_FLAGS OSTCBFlagsRdy; /*Event flags that made task ready to run*/
任务控制块(续2) OS_TCB (in uCOS_II.h) INT16U OSTCBDly; /* Nbr ticks to delay task or, timeout waiting for event */ INT8U OSTCBStat; /* Task status */ INT8U OSTCBPrio; /* Task priority (0: highest, 63: lowest) */ INT8U OSTCBX; INT8U OSTCBY; INT8U OSTCBBitX; INT8U OSTCBBitY; #if OS_TASK_DEL_EN > 0 BOOLEAN OSTCBDelReq; /* Indicates whether a task needs to delete itself */ #endif } OS_TCB;
任务间通信 途径 uC/OS-II提供的机制 全局变量 消息传递 Event, Mail box, Message queue, Semaphore,Mutex
共享与互斥 共享 互斥的方法? 实现任务间通信最简单的办法就是使用共享数据结构。特别是当所有任务都在一个单一地址空间时,这种方法尤其方便 关中断 使用“Test-And-Set”(TAS)操作 某些微处理器(如Motorola 68k系列)具有硬件TAS指令 禁止任务切换 利用信号量
临界区(Critical Region) 禁中断临界区 禁调度临界区 禁共享临界区 OS_ENTER_CRITICAL() OS_EXIT_CRITICAL() (in os_cpu.h) 禁调度临界区 OSSchedLock() OSSchedUnlock() (in os_core.c) 禁共享临界区 Semaphore, etc.
中断 中断:一种硬件机制 中断处理完成后: 异步事件的发生 中断一旦被识别,CPU保存部分或全部现场(context),即部分或全部寄存器的值,跳转到中断服务子程序(ISR)。 中断处理完成后: 对前后台系统,程序回到后台程序 对不可剥夺型内核,程序回到被中断的任务 读可剥夺型内核,重新调度选择任务执行 ->OSIntExit()函数
中断的几个概念 中断延迟 中断响应时间 中断恢复时间 中断延迟 = 关中断的最长时间 + 开始执行中断服务子程序第一条指令的时间 从中断发生到开始执行用户的中断服务程序来处理中断的时间 中断响应时间 = 中断延迟 + 保存CPU内部寄存器的时间 + … 中断恢复时间 微处理器返回到被中断了的程序代码所需要的时间,或返回到更高优先级任务的时间
中断的几个概念(续)
内容 uC/OS-II概述 一些基本概念 内核情景分析 完整的程序 源代码文件结构 任务、任务控制块、任务间通信、共享与互斥、中断 从boot到操作系统,操作系统初始化,任务创建,启动多任务,任务调度,任务切换,任务定时等待… 完整的程序
从boot到操作系统 入口方式 ldr pc,=dummyOs bl CEntry
操作系统初始化 OSInit() (os_core.c) 初始化uC/OS-II所有的变量和数据结构; 创建空闲任务OS_TaskIdle(),该任务优先级最低(OS_LOWEST_PRIO),总处于就绪状态 (可选的)创建统计任务OS_TaskStat(),该任务优先级次低(OS_LOWEST_PRIO - 1)
任务创建 OSTaskCreate() (os_task.c) 附: 数组OSTCBPrioTbl[] (ucos_II.h)
OSTaskCreate() OS_ENTER_CRITICAL(); if (OSTCBPrioTbl[prio] == (OS_TCB *)0) { /* Make sure task doesn't already exist at this priority */ OSTCBPrioTbl[prio] = (OS_TCB *)1; /* Reserve the priority to prevent others from doing */ OS_EXIT_CRITICAL(); psp = (OS_STK *)OSTaskStkInit(task, pdata, ptos, 0); /* Initialize the task's stack */ err = OS_TCBInit(prio, psp, (OS_STK *)0, 0, 0, (void *)0, 0); if (err == OS_NO_ERR) { OSTaskCtr++; /* Increment the #tasks counter */ if (OSRunning == TRUE) { /* Find highest priority task if multitasking has started */ OS_Sched(); } } else { OSTCBPrioTbl[prio] = (OS_TCB *)0;/* Make this priority available to others */ return (err); return (OS_PRIO_EXIST);
启动多任务 OSStart(): (os_core.c) 真正启动操作系统,启动进程和进程调度 OSStartHighRdy() (os_cpu_a.s)
OSStart() void OSStart (void) { INT8U y; INT8U x; if (OSRunning == FALSE) { y = OSUnMapTbl[OSRdyGrp]; /* Find highest priority's task priority number */ x = OSUnMapTbl[OSRdyTbl[y]]; OSPrioHighRdy = (INT8U)((y << 3) + x); OSPrioCur = OSPrioHighRdy; OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; /* Point to highest priority task ready to run */ OSTCBCur = OSTCBHighRdy; OSStartHighRdy(); /* Execute target specific code to start task */ }
任务调度 数据结构: 编组就绪位图OSRdyGrp, 组内就绪位图OSRdyTbl[] (ucos_II.h) OSMapTbl[] (os_core.c) Priority Resolution Table: OSUnMapTbl[] (os_core.c)
调度时机 OSStart(): (os_core.c) 任务级调度和中断级调度 OSStartHighRdy() (os_cpu_a.s) OS_Sched(): (os_core.c) OSIntExit(): (os_core.c)
OS_Sched() #if OS_CRITICAL_METHOD == 3 /* Allocate storage for CPU status register */ OS_CPU_SR cpu_sr; #endif INT8U y; OS_ENTER_CRITICAL(); if ((OSIntNesting == 0) && (OSLockNesting == 0)) { y = OSUnMapTbl[OSRdyGrp]; /* Get pointer to HPT ready to run */ OSPrioHighRdy = (INT8U)((y << 3) + OSUnMapTbl[OSRdyTbl[y]]); if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; /* Increment context switch counter */ OS_TASK_SW(); /* Perform a context switch */ } OS_EXIT_CRITICAL();
OSIntExit() if (OSRunning == TRUE) { OS_ENTER_CRITICAL(); if (OSIntNesting > 0) { /* Prevent OSIntNesting from wrapping */ OSIntNesting--; } if ((OSIntNesting == 0) && (OSLockNesting == 0)) { OSIntExitY = OSUnMapTbl[OSRdyGrp]; OSPrioHighRdy = (INT8U)((OSIntExitY << 3) + OSUnMapTbl[OSRdyTbl[OSIntExitY]]); if (OSPrioHighRdy != OSPrioCur) { /* No Ctx Sw if current task is highest rdy */ OSTCBHighRdy = OSTCBPrioTbl[OSPrioHighRdy]; OSCtxSwCtr++; /* Keep track of the number of ctx switches */ OSIntCtxSw(); /* Perform interrupt level ctx switch */ OS_EXIT_CRITICAL();
任务切换 OSStartHighRdy() (os_cpu_a.s) OS_TASK_SW() (os_cpu_a.s) OSIntCtxSw()
Main()>OSStart()>OSStartHighRdy() EXPORT OSStartHighRdy OSStartHighRdy ; Store the address of OSTCBHighRdy in r0 … LDR sp, [r0] ; switch to the new stack LDMFD sp!, {r0} ; get saved SPSR; popup LDMFD sp!, {r0} ; get CPSR from top of the stack MSR CPSR_cxsf, r0 ; CPSR should be SVC32Mode LDMFD sp!, {r0-r12,pc} ; start the new task END
OS_TASK_SW() EXPORT OS_TASK_SW OS_TASK_SW ; store old STMFD sp!, {r0-r12, lr} ; save register file and ret address MRS r0, CPSR STMFD sp!, {r0} ; save current PSR STMFD sp!, {r0} ; save SPSR ; store old sp in PCB BL GetOSTCBCur; STR sp, [r0] ;the Same as OSStartHighRdy …
进程定时等待 时间管理 SetTickIn5MS() OSTickISR(): OSTimeDly(): (os_time.c) OSTimeTick() OSTimeDly(): (os_time.c)
OSTickISR() IRQ_Hander()完成的任务如下: 保存被中断任务的上下文; 调用函数OSIntEnter(); 调用函数OSTimeTick(); 清中断源; 调用函数OSIntExit(); 如果进行任务切换,则调用IntTaskSwitch(),否则恢复被中断任务的上下文,并返回该任务。
一个完整的程序 完整程序 实验
Highest Priority OS_LOWEST_PRO Wait for ISR… OSStart()