ucos学习笔记

前一段时间笔者学习uCOS-III,第一次接触OS这个概念吧。下面把个人的学习笔记分享出来,仅供参考。

1、前后台系统:后台程序是一个死循环,也称为“ 任务级 ”,前台程序则是中断服务程序,也称为“ 中断级 ”;一般的低成本应用多采用这种程序结构。

2、实时内核:仅为一段软件代码,它把系统功能划分为多个任务,每个任务只完成特定的一个功能,通常都为死循环;CPU在任意时刻只能执行一个任务,但每个任务都认为自己在独自使用整个CPU,由于处理器速度非常快,任务切换的速度也非常快,所以看起来几乎多个任务同时被执行;对多任务的管理就是实时内核所要做的工作,实现CPU资源的最大化利用。

3、uC/OS-III是一个可剥夺型内核(抢占式内核,Preemptive Kernel),它总是执行当前就绪任务中优先级最高的那个;uC/OS-III被设计用于32位处理器; uC/OS-III至少需要4KB RAM资源的微控制器上运行

4、uC/OS-III的特性:

l 同优先级任务的时间片轮转调度
l 任务数目不受限制
l 优先级数目不受限制
l 内核对象数目不受限制
l 可嵌套的任务挂起(suspension),嵌套挂起深度最大可达250层
l 直接向任务发送信号
l 直接向任务发送消息
l 任务级时钟节拍处理
5、临界段代码:不可被打断的代码(在运行这些代码时,需要关闭中断,运行完后再开启)。有两种方式保护临界段代码:1、关中断;2、调度器上锁。两种方法均可以测得中断关闭时间

6、任务的类型有两种:运行至完成型、无限循环型(大多数嵌入式系统采用这种);任务与C函数不同,任务不允许返回;在创建一个任务时,必须为任务分配一个任务控制块(OS_TCB);每一个任务都要有属于自己的栈(类型为CPU_STK);每一个任务都必须调用一个可以引发任务“等待某事件”的函数,比如一段延迟结束(OSTimeDly()或OSTimeDlyHMSM()),在等待一个事件时,它不会占用CPU时间,这是与前后台系统不同的地方;任务的三个重要参数:任务控制模块TCB、优先级、栈空间(存放局部变量、函数调用返回地址、ISR嵌套。如果出现一些很奇怪的结果,首先怀疑占空间是否给小了);每个任务都有自己独立的CPU寄存器

7、uC/OS-III 允许任务停止自身或者停止另外的任务。停止一个任务意味着这个任务将不再执行直到被其他的任务恢复。停止可以被嵌套到250级。换句话说,一个任务可以停止另外的任务多达250次。当然,这个任务必须被恢复同等次数才有资格再次获得CPU。

8、OSInit()会创建2~5个任务,其中空闲任务(OS_IdleTask())和时钟节拍任务(OS_TickTask())是必须要创建的2个任务,其他的统计任务(OS_StatTask())、定时任务(OS_TmrTask())和中断处理队列管理任务(OS_IntQTask())由#define使能;main函数必须在调用其他任务uC/OS-III函数之前调用OSInit()

l 空闲任务(OS_IdleTask(),os_core.c)
【必须创建】uC/OS-III创建的第一个任务,优先级总是OS_CFG_PRIO_MAX-1(最低优先级)

真正的无限循环,即不会调用任务会使其进入等待状态的服务函数

l 时钟节拍任务(OS_TickTask(),os_tick.c)
【必须创建】该任务的优先级应当只比用户的系统中最重要的任务的优先级略低一点[注:P77]

时钟节拍列表=数据表(OSCfg_TickWheel[])+计数器(OSTickCtr),数据表的表项数目OS_CFG_TICK_WHEEL_SIZE一般设置为任务数的1/4左右,且最好为素数;每当时钟节拍任务接收到时钟节拍ISR发送的信号量时,OSTickCtr自动+1

每次节拍中断发生时,只有其中一个表项上的任务可能延迟结束

l 统计任务(OS_StatTask(),os_stat.c)
总CPU利用率、各任务的CPU利用率和各任务的堆栈使用量

在main()函数创建的第一个也是唯一一个应用任务中调用OSStatTaskCPUUsageInit(),这个应用任务应该分配一个很高的优先级(0除外);uC/OS-III允许用户在调用OSStart()之前创建任意数目的任务,然而,要使用统计任务时,只能创建一个任务,在该任务中调用OSStatTaskCPUUsageInit()。

l 定时任务(OS_TmrTask(),os_tmr.c)
一个递减的计数器,减为0时,可以启动一个用户自定义的回调函数(Callback Funcion),避免在回调函数中使用阻塞调用(OSTimeDly()、OSTimeDlyHMSM()、OS???Pend()),回调函数的执行实在定时器任务内完成的;与时钟节拍任务使用相同的中断源

优先级一般为中等优先级

l 中断处理队列管理任务(OS_IntQTask(),os_int.c)
相当于在ISR和任务中插入了一脚,负责“延迟”(deferring)在ISR中调用的系统post服务函数的行为[注:P86]

优先级永远为最高的0,系统保留优先级

9、通过OSTaskCreate()函数创建一个任务,该任务有13个参数

10、任务的优先级:数值越小,优先级越高,其范围为1~(OS_CFG_PRIO_MAX-2);0和OS_CFG_PRIO_MAX-1为两个保留的优先级

11、多任务系统管理函数OSStart()一般为main函数的最后一步,注意:uC/OS-III会首先运行在调用OSStart()之前已经创建的优先级最高的任务,并且可以创建任务数量的任务,但是建议只创建一个任务[注:P33]

12、任务状态:从用户角度来看,任务的状态只有5种:休眠态、就绪态、运行态、等待态和中断服务态;在uC/OS-III内部,状态则有8种:延迟、就绪、带超时检测的等待、等待、延迟被挂起、被挂起、带超时检测的等待且被挂起和等待且被挂起

13、任务被删除OSTaskDel(),代表任务进入休眠态,使得该代码无法获得CPU的使用权

14、一个任务可以创建其它任务(OSTaskCreate())、停止或者恢复其它(OSTaskSuspned()和OSTaskResume())、提交信号量到其它任务、发送消息到其它任务、提供共享资源等。换句话说,任务不是只被限制于“等待事件”。

15、就绪表=就绪优先级位映射表OSPrioTbl[](标明哪个优先级下有任务就绪)+就绪任务列表OSRdyList[](包含指向就绪任务的指针)

16、拥有“ 计算前导零 ”(CLZ , Count Leading Zeros)的处理器可以加速查找最高优先级任务的过程

17、uC/OS-III有两种不同方法处理中断服务程序发布的事件:直接发布(direct post)和延迟发布(deferred post),其最终的结果都是高优先级的任务获得了CPU控制权,但是延迟发布多了一个中断处理任务,ISR发布消息后由ISR hander task接管,而非“直接发布”中的直接跳转到高优先级的任务中(这样做是为了避免使用关中断的方法来保护临界段代码);直接发布,uC/OS-III必须关闭中断以保护临界段代码,防止中断处理程序访问这些临界段代码;延迟发布,uC/OS-III通过给任务调度器上锁的方式来保护临界段代码

18、时间片轮转调度:当两个或更多的任务拥有相同的优先级时,uC/OS-III允许一个任务运行一段指定的时间,然后轮到下一个任务;如果一个任务不需要执行完时间片,则主动放弃CPU的控制权让下一个任务运行,称为“礼让”(yielding)

19、任务调度通过两个函数来执行:OSSched()(任务级代码使用)和OSIntExit()(中断服务程序结束时使用);OSSched()的任务切换部分的工作由OSCtxSw()完成,OSIntExit( )中任务的切换工作由OSIntCtxSw()完成

20、时钟节拍通常频率设置为10~1000Hz,过高的频率会增加系统的额外开销

21、当一个任务等待信号量、互斥型信号量、事件标志组和消息队列时,该任务就被放入任务挂起表(pend lists)或等待表中(wait lists)

22、常见时间服务用户程序:

OSTimeDly()任务延时n个时钟节拍;三种模式(相对模式、周期模式、绝对模式,推荐使用周期模式以获得长时间运行的周期性延迟,比如周期扫描键盘,周期采集某传感器的值,绝对模式一般用于上电后指定的时间执行具体动作,比如上电10s后关闭某盏灯)

OSTimeDlyHMSM() 任务延时指定的时间,采用“时:分:秒:毫秒”方式;延迟精度极大取决与时钟节拍频率,如果频率设定为1000Hz,那么延时的精度为1ms

OSTimeDlyResume()恢复被延迟的任务;谨慎使用,因为它无法分辨是延时结束还是被恢复

OSTimeGet() 获取当前时钟节拍计数器的值

OSTimeSet() 设置时钟节拍计数器的值

OSTimeTick() 触发一次时钟节拍任务;每次时钟节拍中断到来时,中断服务程序必须调用该函数

23、uC/OS-III中定时器的时间分辨率由定时器任务频率来配置(OS_CFG_TMR_TASK_RATE_HZ),若定时器任务频率为10Hz,那么所有定时器的时间分辨率为1/10s;定时器的运行模式有三种:单次定时器、周期定时器(无初始延迟)、周期定时器(有初始延迟)

24、最常用的独占共享资源和创建临界区的方法:

l关中断不推荐,会增加系统中断延迟;而这种方法也是任务和中断服务程序共享变量的唯一方法
l 禁止任务调度 不推荐,有悖于可剥夺型内核的设计初衷,因为该任务即为最高优先级任务
l 使用信号量 速度比互斥型信号量稍快一些,但是会遇到优先级反转问题
l 使用互斥型信号量(mutex) 比信号量稍慢,但是解决了优先级反转问题
25、信号量就像一种上锁机制,必须获得对应的钥匙才能执行代码;信号量通常分为两种:二进制信号量、计数型信号量;在共享资源时,只有任务才能使用信号量,中断服务程序则不能使用,当用信号量发送信号时,则没有限制;信号量一般用于访问I/O设备

26、在使用信号量之前必须先创建信号量

27、无界优先级反转问题:低优先级的任务正在访问某共享资源,此时高优先级的任务进行内核剥夺,抢走CPU使用权,但是高优先级也要访问该共享资源,因此调度器再次转回低优先级任务等待其释放信号量,如果此时有中优先级的任务插入,那么调度器会直接进入该任务,忽略高优先级的任务。导致的结果就是高优先级反转。解决办法:互斥型信号量(mutex),原理为将使用共享资源的低优先级的任务提升至与高优先级任务一样,此时调度器再次返回低优先级任务,等待其执行完毕释放信号量后回到高优先级处继续执行,中优先级则不会插入。因此,低优先级的任务访问共享资源的速度越快越好

28、如果没有任务对共享资源的访问有截止时间,那么普通信号量就可以替代互斥型信号量

29、死锁(deadlock)也称为抱死(deadly embrace),指两个任务无限制的等待对方控制的资源。解决办法:

l 先得到全部资源,再进行下一步操作
l 用相同的顺序申请多个资源
l 在调用请求信号量的函数时设定超过时间
30、uC/OS-III中有两种基本的同步机制:信号量和事件标志(第三种为任务信号量)

31、任务或者ISR可以使用信号量同步任务,这种同步操作叫做单向同步;当多个任务等待同一个信号量时,可以在发布信号时指定OS_OPT_POST_ALL开启“广播”功能;任务或者ISR也可以使用任务信号量同步任务,这种方法比较常用,在uC/OS-III中,每个任务都有自己的内嵌信号量;事件标志给与了任务同步更多的同步选择和方法,在事件标志组(OS_FLAG_GRP)中可以用“与AND”“或OR”产生更多的不同事件(flag)组合,注意:事件标志组必须在启动uC/OS-III 之前创建

32、如果任务或者ISR需要给单个任务发信号,则使用任务信号量更为恰当,因为此时不需要再声明一个外部信号量对象,而且任务信号服务函数的执行速度比信号服务函数的执行速度要快

33、消息传递:任务或者中断服务程序需要交流信息,这种交流有两种途径:

l 全局变量
如果一个资源有中断服务程序的参与,唯一保证对共享变量独占访问的方法就是关中断

如果只是两个任务共享数据:关中断、给调度器上锁、使用信号量或者使用互斥型信号量(推荐)

注意:任务想要与中断服务通讯,只能通过全局变量

l 发布消息
34、消息队列的相关函数,中断服务程序只能调用OSQPost();消息队列的读取一般采用先进先出(FIFO),有时候也会使用后进先出(LIFO)的方式;消息的获取可以设置按照优先级或者广播

35、更为广泛使用的消息队列一般为任务内建的消息队列(更高效,不用创建外部消息队列,如同信号量与任务信号量的关系)。

36、同事件标志组,消息队列也要求在启动uC/OS-III 之前创建

37、发送的消息通常是一个指针,这些数据必须保持不变,直到接收者完成相应的数据处理

38、移植uC/OS-III需要修改的文件:

l uC/CPU:cpu.h(数据类型与字长)、cpu_c.c(中断控制器)、cpu_a.asm(开、关中断,CLZ指令)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章